]> git.sesse.net Git - vlc/blob - modules/codec/spudec/parse.c
Merge branch 'master' into lpcm_encoder
[vlc] / modules / codec / spudec / parse.c
1 /*****************************************************************************
2  * parse.c: SPU parser
3  *****************************************************************************
4  * Copyright (C) 2000-2001, 2005, 2006 the VideoLAN team
5  * $Id$
6  *
7  * Authors: Sam Hocevar <sam@zoy.org>
8  *          Laurent Aimar <fenrir@via.ecp.fr>
9  *          Gildas Bazin <gbazin@videolan.org>
10  *
11  * This program is free software; you can redistribute it and/or modify
12  * it under the terms of the GNU General Public License as published by
13  * the Free Software Foundation; either version 2 of the License, or
14  * (at your option) any later version.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program; if not, write to the Free Software
23  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
24  *****************************************************************************/
25
26 /*****************************************************************************
27  * Preamble
28  *****************************************************************************/
29 #ifdef HAVE_CONFIG_H
30 # include "config.h"
31 #endif
32
33 #include <vlc_common.h>
34 #include <vlc_codec.h>
35 #include <vlc_input.h>
36
37 #include "spudec.h"
38
39 /*****************************************************************************
40  * Local prototypes.
41  *****************************************************************************/
42 typedef struct
43 {
44     int i_width;
45     int i_height;
46     int i_x;
47     int i_y;
48 } spu_properties_t;
49
50 typedef struct
51 {
52     int   pi_offset[2];                              /* byte offsets to data */
53     uint16_t *p_data;
54
55     /* Color information */
56     bool b_palette;
57     uint8_t    pi_alpha[4];
58     uint8_t    pi_yuv[4][3];
59
60     /* Auto crop fullscreen subtitles */
61     bool b_auto_crop;
62     int i_y_top_offset;
63     int i_y_bottom_offset;
64
65 } subpicture_data_t;
66
67 static int  ParseControlSeq( decoder_t *, subpicture_t *, subpicture_data_t *,
68                              spu_properties_t *, mtime_t i_pts );
69 static int  ParseRLE       ( decoder_t *, subpicture_data_t *,
70                              const spu_properties_t * );
71 static void Render         ( decoder_t *, subpicture_t *, subpicture_data_t *,
72                              const spu_properties_t * );
73
74 /*****************************************************************************
75  * AddNibble: read a nibble from a source packet and add it to our integer.
76  *****************************************************************************/
77 static inline unsigned int AddNibble( unsigned int i_code,
78                                       const uint8_t *p_src, unsigned int *pi_index )
79 {
80     if( *pi_index & 0x1 )
81     {
82         return( i_code << 4 | ( p_src[(*pi_index)++ >> 1] & 0xf ) );
83     }
84     else
85     {
86         return( i_code << 4 | p_src[(*pi_index)++ >> 1] >> 4 );
87     }
88 }
89
90 /*****************************************************************************
91  * ParsePacket: parse an SPU packet and send it to the video output
92  *****************************************************************************
93  * This function parses the SPU packet and, if valid, sends it to the
94  * video output.
95  *****************************************************************************/
96 subpicture_t * ParsePacket( decoder_t *p_dec )
97 {
98     decoder_sys_t *p_sys = p_dec->p_sys;
99     subpicture_t *p_spu;
100     subpicture_data_t spu_data;
101     spu_properties_t spu_properties;
102
103     /* Allocate the subpicture internal data. */
104     p_spu = decoder_NewSubpicture( p_dec, NULL );
105     if( !p_spu ) return NULL;
106
107     p_spu->i_original_picture_width =
108         p_dec->fmt_in.subs.spu.i_original_frame_width;
109     p_spu->i_original_picture_height =
110         p_dec->fmt_in.subs.spu.i_original_frame_height;
111
112     /* Getting the control part */
113     if( ParseControlSeq( p_dec, p_spu, &spu_data, &spu_properties, p_sys->i_pts ) )
114     {
115         /* There was a parse error, delete the subpicture */
116         decoder_DeleteSubpicture( p_dec, p_spu );
117         return NULL;
118     }
119
120     /* we are going to expand the RLE stuff so that we won't need to read
121      * nibbles later on. This will speed things up a lot. Plus, we'll only
122      * need to do this stupid interlacing stuff once.
123      *
124      * Rationale for the "p_spudec->i_rle_size * 4*sizeof(*spu_data.p_data)":
125      *  one byte gaves two nibbles and may be used twice (once per field)
126      * generating 4 codes.
127      */
128     spu_data.p_data = malloc( sizeof(*spu_data.p_data) * 2 * 2 * p_sys->i_rle_size );
129
130     /* We try to display it */
131     if( ParseRLE( p_dec, &spu_data, &spu_properties ) )
132     {
133         /* There was a parse error, delete the subpicture */
134         decoder_DeleteSubpicture( p_dec, p_spu );
135         free( spu_data.p_data );
136         return NULL;
137     }
138
139 #ifdef DEBUG_SPUDEC
140     msg_Dbg( p_dec, "total size: 0x%x, RLE offsets: 0x%x 0x%x",
141              p_sys->i_spu_size,
142              spu_data.pi_offset[0], spu_data.pi_offset[1] );
143 #endif
144
145     Render( p_dec, p_spu, &spu_data, &spu_properties );
146     free( spu_data.p_data );
147
148     return p_spu;
149 }
150
151 /*****************************************************************************
152  * ParseControlSeq: parse all SPU control sequences
153  *****************************************************************************
154  * This is the most important part in SPU decoding. We get dates, palette
155  * information, coordinates, and so on. For more information on the
156  * subtitles format, see http://sam.zoy.org/doc/dvd/subtitles/index.html
157  *****************************************************************************/
158 static int ParseControlSeq( decoder_t *p_dec, subpicture_t *p_spu,
159                             subpicture_data_t *p_spu_data, spu_properties_t *p_spu_properties, mtime_t i_pts )
160 {
161     decoder_sys_t *p_sys = p_dec->p_sys;
162
163     /* Our current index in the SPU packet */
164     unsigned int i_index;
165
166     /* The next start-of-control-sequence index and the previous one */
167     unsigned int i_next_seq = 0, i_cur_seq = 0;
168
169     /* Command and date */
170     uint8_t i_command = SPU_CMD_END;
171     mtime_t date = 0;
172     bool b_cmd_offset = false;
173     bool b_cmd_alpha = false;
174     subpicture_data_t spu_data_cmd;
175
176     if( !p_spu || !p_spu_data )
177         return VLC_EGENERIC;
178
179     /* Create working space for spu data */
180     memset( &spu_data_cmd, 0, sizeof(spu_data_cmd) );
181     spu_data_cmd.pi_offset[0] = -1;
182     spu_data_cmd.pi_offset[1] = -1;
183     spu_data_cmd.p_data = NULL;
184     spu_data_cmd.b_palette = false;
185     spu_data_cmd.b_auto_crop = false;
186     spu_data_cmd.i_y_top_offset = 0;
187     spu_data_cmd.i_y_bottom_offset = 0;
188     spu_data_cmd.pi_alpha[0] = 0x00;
189     spu_data_cmd.pi_alpha[1] = 0x0f;
190     spu_data_cmd.pi_alpha[2] = 0x0f;
191     spu_data_cmd.pi_alpha[3] = 0x0f;
192
193     /* Initialize the structure */
194     p_spu->i_start = p_spu->i_stop = 0;
195     p_spu->b_ephemer = false;
196
197     memset( p_spu_properties, 0, sizeof(*p_spu_properties) );
198
199     /* */
200     *p_spu_data = spu_data_cmd;
201
202     for( i_index = 4 + p_sys->i_rle_size; i_index < p_sys->i_spu_size ; )
203     {
204         /* If we just read a command sequence, read the next one;
205          * otherwise, go on with the commands of the current sequence. */
206         if( i_command == SPU_CMD_END )
207         {
208             if( i_index + 4 > p_sys->i_spu_size )
209             {
210                 msg_Err( p_dec, "overflow in SPU command sequence" );
211                 return VLC_EGENERIC;
212             }
213
214             /* */
215             b_cmd_offset = false;
216             b_cmd_alpha = false;
217             /* Get the control sequence date */
218             date = (mtime_t)GetWBE( &p_sys->buffer[i_index] ) * 11000;
219
220             /* Next offset */
221             i_cur_seq = i_index;
222             i_next_seq = GetWBE( &p_sys->buffer[i_index+2] );
223
224             if( i_next_seq > p_sys->i_spu_size )
225             {
226                 msg_Err( p_dec, "overflow in SPU next command sequence" );
227                 return VLC_EGENERIC;
228             }
229
230             /* Skip what we just read */
231             i_index += 4;
232         }
233
234         i_command = p_sys->buffer[i_index];
235
236         switch( i_command )
237         {
238         case SPU_CMD_FORCE_DISPLAY: /* 00 (force displaying) */
239             p_spu->i_start = i_pts + date;
240             p_spu->b_ephemer = true;
241             i_index += 1;
242             break;
243
244         /* Convert the dates in seconds to PTS values */
245         case SPU_CMD_START_DISPLAY: /* 01 (start displaying) */
246             p_spu->i_start = i_pts + date;
247             i_index += 1;
248             break;
249
250         case SPU_CMD_STOP_DISPLAY: /* 02 (stop displaying) */
251             p_spu->i_stop = i_pts + date;
252             i_index += 1;
253             break;
254
255         case SPU_CMD_SET_PALETTE:
256             /* 03xxxx (palette) */
257             if( i_index + 3 > p_sys->i_spu_size )
258             {
259                 msg_Err( p_dec, "overflow in SPU command" );
260                 return VLC_EGENERIC;
261             }
262
263             if( p_dec->fmt_in.subs.spu.palette[0] == 0xBeeF )
264             {
265                 unsigned int idx[4];
266                 int i;
267
268                 spu_data_cmd.b_palette = true;
269
270                 idx[0] = (p_sys->buffer[i_index+1]>>4)&0x0f;
271                 idx[1] = (p_sys->buffer[i_index+1])&0x0f;
272                 idx[2] = (p_sys->buffer[i_index+2]>>4)&0x0f;
273                 idx[3] = (p_sys->buffer[i_index+2])&0x0f;
274
275                 for( i = 0; i < 4 ; i++ )
276                 {
277                     uint32_t i_color = p_dec->fmt_in.subs.spu.palette[1+idx[i]];
278
279                     /* FIXME: this job should be done sooner */
280                     spu_data_cmd.pi_yuv[3-i][0] = (i_color>>16) & 0xff;
281                     spu_data_cmd.pi_yuv[3-i][1] = (i_color>>0) & 0xff;
282                     spu_data_cmd.pi_yuv[3-i][2] = (i_color>>8) & 0xff;
283                 }
284             }
285
286             i_index += 3;
287             break;
288
289         case SPU_CMD_SET_ALPHACHANNEL: /* 04xxxx (alpha channel) */
290             if( i_index + 3 > p_sys->i_spu_size )
291             {
292                 msg_Err( p_dec, "overflow in SPU command" );
293                 return VLC_EGENERIC;
294             }
295
296             if(!p_sys->b_disabletrans)
297             { /* If we want to use original transparency values */
298                 b_cmd_alpha = true;
299                 spu_data_cmd.pi_alpha[3] = (p_sys->buffer[i_index+1]>>4)&0x0f;
300                 spu_data_cmd.pi_alpha[2] = (p_sys->buffer[i_index+1])&0x0f;
301                 spu_data_cmd.pi_alpha[1] = (p_sys->buffer[i_index+2]>>4)&0x0f;
302                 spu_data_cmd.pi_alpha[0] = (p_sys->buffer[i_index+2])&0x0f;
303             }
304
305             i_index += 3;
306             break;
307
308         case SPU_CMD_SET_COORDINATES: /* 05xxxyyyxxxyyy (coordinates) */
309             if( i_index + 7 > p_sys->i_spu_size )
310             {
311                 msg_Err( p_dec, "overflow in SPU command" );
312                 return VLC_EGENERIC;
313             }
314
315             p_spu_properties->i_x = (p_sys->buffer[i_index+1]<<4)|
316                          ((p_sys->buffer[i_index+2]>>4)&0x0f);
317             p_spu_properties->i_width = (((p_sys->buffer[i_index+2]&0x0f)<<8)|
318                               p_sys->buffer[i_index+3]) - p_spu_properties->i_x + 1;
319
320             p_spu_properties->i_y = (p_sys->buffer[i_index+4]<<4)|
321                          ((p_sys->buffer[i_index+5]>>4)&0x0f);
322             p_spu_properties->i_height = (((p_sys->buffer[i_index+5]&0x0f)<<8)|
323                               p_sys->buffer[i_index+6]) - p_spu_properties->i_y + 1;
324
325             /* Auto crop fullscreen subtitles */
326             if( p_spu_properties->i_height > 250 )
327                 p_spu_data->b_auto_crop = true;
328
329             i_index += 7;
330             break;
331
332         case SPU_CMD_SET_OFFSETS: /* 06xxxxyyyy (byte offsets) */
333             if( i_index + 5 > p_sys->i_spu_size )
334             {
335                 msg_Err( p_dec, "overflow in SPU command" );
336                 return VLC_EGENERIC;
337             }
338
339             b_cmd_offset = true;
340             p_spu_data->pi_offset[0] = GetWBE(&p_sys->buffer[i_index+1]) - 4;
341             p_spu_data->pi_offset[1] = GetWBE(&p_sys->buffer[i_index+3]) - 4;
342             i_index += 5;
343             break;
344
345         case SPU_CMD_END: /* ff (end) */
346             if( b_cmd_offset )
347             {
348                 /* It seems that palette and alpha from the block having
349                  * the cmd offset have to be used
350                  * XXX is it all ? */
351                 p_spu_data->b_palette = spu_data_cmd.b_palette;
352                 if( spu_data_cmd.b_palette )
353                     memcpy( p_spu_data->pi_yuv, spu_data_cmd.pi_yuv, sizeof(spu_data_cmd.pi_yuv) );
354                 if( b_cmd_alpha )
355                     memcpy( p_spu_data->pi_alpha, spu_data_cmd.pi_alpha, sizeof(spu_data_cmd.pi_alpha) );
356             }
357
358             i_index += 1;
359             break;
360
361         default: /* xx (unknown command) */
362             msg_Warn( p_dec, "unknown SPU command 0x%.2x", i_command );
363             if( i_index + 1 < i_next_seq )
364             {
365                  /* There is at least one other command sequence */
366                  if( p_sys->buffer[i_next_seq - 1] == SPU_CMD_END )
367                  {
368                      /* This is consistent. Skip to that command sequence. */
369                      i_index = i_next_seq;
370                  }
371                  else
372                  {
373                      /* There were other commands. */
374                      msg_Warn( p_dec, "cannot recover, dropping subtitle" );
375                      return VLC_EGENERIC;
376                  }
377             }
378             else
379             {
380                 /* We were in the last command sequence. Stop parsing by
381                  * pretending we met an SPU_CMD_END command. */
382                 i_command = SPU_CMD_END;
383                 i_index++;
384             }
385         }
386
387         /* */
388         if( i_command == SPU_CMD_END && i_index != i_next_seq )
389             break;
390     }
391
392     /* Check that the next sequence index matches the current one */
393     if( i_next_seq != i_cur_seq )
394     {
395         msg_Err( p_dec, "index mismatch (0x%.4x != 0x%.4x)",
396                  i_next_seq, i_cur_seq );
397         return VLC_EGENERIC;
398     }
399
400     if( i_index > p_sys->i_spu_size )
401     {
402         msg_Err( p_dec, "uh-oh, we went too far (0x%.4x > 0x%.4x)",
403                  i_index, p_sys->i_spu_size );
404         return VLC_EGENERIC;
405     }
406
407     const int i_spu_size = p_sys->i_spu - 4;
408     if( p_spu_data->pi_offset[0] < 0 || p_spu_data->pi_offset[0] >= i_spu_size ||
409         p_spu_data->pi_offset[1] < 0 || p_spu_data->pi_offset[1] >= i_spu_size )
410     {
411         msg_Err( p_dec, "invalid offset values" );
412         return VLC_EGENERIC;
413     }
414
415     if( !p_spu->i_start )
416     {
417         msg_Err( p_dec, "no `start display' command" );
418         return VLC_EGENERIC;
419     }
420
421     if( p_spu->i_stop <= p_spu->i_start && !p_spu->b_ephemer )
422     {
423         /* This subtitle will live for 5 seconds or until the next subtitle */
424         p_spu->i_stop = p_spu->i_start + (mtime_t)500 * 11000;
425         p_spu->b_ephemer = true;
426     }
427
428     /* Get rid of padding bytes */
429     if( p_sys->i_spu_size > i_index + 1 )
430     {
431         /* Zero or one padding byte are quite usual
432          * More than one padding byte - this is very strange, but
433          * we can ignore them. */
434         msg_Warn( p_dec, "%i padding bytes, we usually get 0 or 1 of them",
435                   p_sys->i_spu_size - i_index );
436     }
437
438     /* Successfully parsed ! */
439     return VLC_SUCCESS;
440 }
441
442 /*****************************************************************************
443  * ParseRLE: parse the RLE part of the subtitle
444  *****************************************************************************
445  * This part parses the subtitle graphical data and stores it in a more
446  * convenient structure for later decoding. For more information on the
447  * subtitles format, see http://sam.zoy.org/doc/dvd/subtitles/index.html
448  *****************************************************************************/
449 static int ParseRLE( decoder_t *p_dec,
450                      subpicture_data_t *p_spu_data,
451                      const spu_properties_t *p_spu_properties )
452 {
453     decoder_sys_t *p_sys = p_dec->p_sys;
454
455     const unsigned int i_width = p_spu_properties->i_width;
456     const unsigned int i_height = p_spu_properties->i_height;
457     unsigned int i_x, i_y;
458
459     uint16_t *p_dest = p_spu_data->p_data;
460
461     /* The subtitles are interlaced, we need two offsets */
462     unsigned int  i_id = 0;                   /* Start on the even SPU layer */
463     unsigned int  pi_table[ 2 ];
464     unsigned int *pi_offset;
465
466     /* Cropping */
467     bool b_empty_top = true;
468     unsigned int i_skipped_top = 0, i_skipped_bottom = 0;
469     unsigned int i_transparent_code = 0;
470
471     /* Colormap statistics */
472     int i_border = -1;
473     int stats[4]; stats[0] = stats[1] = stats[2] = stats[3] = 0;
474
475     pi_table[ 0 ] = p_spu_data->pi_offset[ 0 ] << 1;
476     pi_table[ 1 ] = p_spu_data->pi_offset[ 1 ] << 1;
477
478     for( i_y = 0 ; i_y < i_height ; i_y++ )
479     {
480         unsigned int i_code;
481         pi_offset = pi_table + i_id;
482
483         for( i_x = 0 ; i_x < i_width ; i_x += i_code >> 2 )
484         {
485             i_code = 0;
486             for( unsigned int i_min = 1; i_min <= 0x40 && i_code < i_min; i_min <<= 2 )
487             {
488                 if( (*pi_offset >> 1) >= p_sys->i_spu_size )
489                 {
490                     msg_Err( p_dec, "out of bounds while reading rle" );
491                     return VLC_EGENERIC;
492                 }
493                 i_code = AddNibble( i_code, &p_sys->buffer[4], pi_offset );
494             }
495             if( i_code < 0x0004 )
496             {
497                 /* If the 14 first bits are set to 0, then it's a
498                  * new line. We emulate it. */
499                 i_code |= ( i_width - i_x ) << 2;
500             }
501
502             if( ( (i_code >> 2) + i_x + i_y * i_width ) > i_height * i_width )
503             {
504                 msg_Err( p_dec, "out of bounds, %i at (%i,%i) is out of %ix%i",
505                          i_code >> 2, i_x, i_y, i_width, i_height );
506                 return VLC_EGENERIC;
507             }
508
509             /* Try to find the border color */
510             if( p_spu_data->pi_alpha[ i_code & 0x3 ] != 0x00 )
511             {
512                 i_border = i_code & 0x3;
513                 stats[i_border] += i_code >> 2;
514             }
515
516             /* Auto crop subtitles (a lot more optimized) */
517             if( p_spu_data->b_auto_crop )
518             {
519                 if( !i_y )
520                 {
521                     /* We assume that if the first line is transparent, then
522                      * it is using the palette index for the
523                      * (background) transparent color */
524                     if( (i_code >> 2) == i_width &&
525                         p_spu_data->pi_alpha[ i_code & 0x3 ] == 0x00 )
526                     {
527                         i_transparent_code = i_code;
528                     }
529                     else
530                     {
531                         p_spu_data->b_auto_crop = false;
532                     }
533                 }
534
535                 if( i_code == i_transparent_code )
536                 {
537                     if( b_empty_top )
538                     {
539                         /* This is a blank top line, we skip it */
540                       i_skipped_top++;
541                     }
542                     else
543                     {
544                         /* We can't be sure the current lines will be skipped,
545                          * so we store the code just in case. */
546                       *p_dest++ = i_code;
547                       i_skipped_bottom++;
548                     }
549                 }
550                 else
551                 {
552                     /* We got a valid code, store it */
553                     *p_dest++ = i_code;
554
555                     /* Valid code means no blank line */
556                     b_empty_top = false;
557                     i_skipped_bottom = 0;
558                 }
559             }
560             else
561             {
562                 *p_dest++ = i_code;
563             }
564         }
565
566         /* Check that we didn't go too far */
567         if( i_x > i_width )
568         {
569             msg_Err( p_dec, "i_x overflowed, %i > %i", i_x, i_width );
570             return VLC_EGENERIC;
571         }
572
573         /* Byte-align the stream */
574         if( *pi_offset & 0x1 )
575         {
576             (*pi_offset)++;
577         }
578
579         /* Swap fields */
580         i_id = ~i_id & 0x1;
581     }
582
583     /* We shouldn't get any padding bytes */
584     if( i_y < i_height )
585     {
586         msg_Err( p_dec, "padding bytes found in RLE sequence" );
587         msg_Err( p_dec, "send mail to <sam@zoy.org> if you "
588                         "want to help debugging this" );
589
590         /* Skip them just in case */
591         while( i_y < i_height )
592         {
593             *p_dest++ = i_width << 2;
594             i_y++;
595         }
596
597         return VLC_EGENERIC;
598     }
599
600 #ifdef DEBUG_SPUDEC
601     msg_Dbg( p_dec, "valid subtitle, size: %ix%i, position: %i,%i",
602              p_spu->i_width, p_spu->i_height, p_spu->i_x, p_spu->i_y );
603 #endif
604
605     /* Crop if necessary */
606     if( i_skipped_top || i_skipped_bottom )
607     {
608 #ifdef DEBUG_SPUDEC
609         int i_y = p_spu->i_y + i_skipped_top;
610         int i_height = p_spu->i_height - (i_skipped_top + i_skipped_bottom);
611 #endif
612         p_spu_data->i_y_top_offset = i_skipped_top;
613         p_spu_data->i_y_bottom_offset = i_skipped_bottom;
614 #ifdef DEBUG_SPUDEC
615         msg_Dbg( p_dec, "cropped to: %ix%i, position: %i,%i",
616                  p_spu->i_width, i_height, p_spu->i_x, i_y );
617 #endif
618     }
619
620     /* Handle color if no palette was found */
621     if( !p_spu_data->b_palette )
622     {
623         int i, i_inner = -1, i_shade = -1;
624
625         /* Set the border color */
626         p_spu_data->pi_yuv[i_border][0] = 0x00;
627         p_spu_data->pi_yuv[i_border][1] = 0x80;
628         p_spu_data->pi_yuv[i_border][2] = 0x80;
629         stats[i_border] = 0;
630
631         /* Find the inner colors */
632         for( i = 0 ; i < 4 && i_inner == -1 ; i++ )
633         {
634             if( stats[i] )
635             {
636                 i_inner = i;
637             }
638         }
639
640         for(       ; i < 4 && i_shade == -1 ; i++ )
641         {
642             if( stats[i] )
643             {
644                 if( stats[i] > stats[i_inner] )
645                 {
646                     i_shade = i_inner;
647                     i_inner = i;
648                 }
649                 else
650                 {
651                     i_shade = i;
652                 }
653             }
654         }
655
656         /* Set the inner color */
657         if( i_inner != -1 )
658         {
659             p_spu_data->pi_yuv[i_inner][0] = 0xff;
660             p_spu_data->pi_yuv[i_inner][1] = 0x80;
661             p_spu_data->pi_yuv[i_inner][2] = 0x80;
662         }
663
664         /* Set the anti-aliasing color */
665         if( i_shade != -1 )
666         {
667             p_spu_data->pi_yuv[i_shade][0] = 0x80;
668             p_spu_data->pi_yuv[i_shade][1] = 0x80;
669             p_spu_data->pi_yuv[i_shade][2] = 0x80;
670         }
671
672 #ifdef DEBUG_SPUDEC
673         msg_Dbg( p_dec, "using custom palette (border %i, inner %i, shade %i)",
674                  i_border, i_inner, i_shade );
675 #endif
676     }
677
678     return VLC_SUCCESS;
679 }
680
681 static void Render( decoder_t *p_dec, subpicture_t *p_spu,
682                     subpicture_data_t *p_spu_data,
683                     const spu_properties_t *p_spu_properties )
684 {
685     uint8_t *p_p;
686     int i_x, i_y, i_len, i_color, i_pitch;
687     const uint16_t *p_source = p_spu_data->p_data;
688     video_format_t fmt;
689     video_palette_t palette;
690
691     /* Create a new subpicture region */
692     memset( &fmt, 0, sizeof(video_format_t) );
693     fmt.i_chroma = VLC_CODEC_YUVP;
694     fmt.i_sar_num = 0; /* 0 means use aspect ratio of background video */
695     fmt.i_sar_den = 1;
696     fmt.i_width = fmt.i_visible_width = p_spu_properties->i_width;
697     fmt.i_height = fmt.i_visible_height = p_spu_properties->i_height -
698         p_spu_data->i_y_top_offset - p_spu_data->i_y_bottom_offset;
699     fmt.i_x_offset = fmt.i_y_offset = 0;
700     fmt.p_palette = &palette;
701     fmt.p_palette->i_entries = 4;
702     for( i_x = 0; i_x < fmt.p_palette->i_entries; i_x++ )
703     {
704         fmt.p_palette->palette[i_x][0] = p_spu_data->pi_yuv[i_x][0];
705         fmt.p_palette->palette[i_x][1] = p_spu_data->pi_yuv[i_x][1];
706         fmt.p_palette->palette[i_x][2] = p_spu_data->pi_yuv[i_x][2];
707         fmt.p_palette->palette[i_x][3] = p_spu_data->pi_alpha[i_x] * 0x11;
708     }
709
710     p_spu->p_region = subpicture_region_New( &fmt );
711     if( !p_spu->p_region )
712     {
713         msg_Err( p_dec, "cannot allocate SPU region" );
714         return;
715     }
716
717     p_spu->p_region->i_x = p_spu_properties->i_x;
718     p_spu->p_region->i_y = p_spu_properties->i_y + p_spu_data->i_y_top_offset;
719     p_p = p_spu->p_region->p_picture->p->p_pixels;
720     i_pitch = p_spu->p_region->p_picture->p->i_pitch;
721
722     /* Draw until we reach the bottom of the subtitle */
723     for( i_y = 0; i_y < (int)fmt.i_height * i_pitch; i_y += i_pitch )
724     {
725         /* Draw until we reach the end of the line */
726         for( i_x = 0 ; i_x < (int)fmt.i_width; i_x += i_len )
727         {
728             /* Get the RLE part, then draw the line */
729             i_color = *p_source & 0x3;
730             i_len = *p_source++ >> 2;
731             memset( p_p + i_x + i_y, i_color, i_len );
732         }
733     }
734 }