* modules/codec/spudec/*: modified the spu decoder to handle text subtitles.
[vlc.git] / modules / codec / spudec / render.c
1 /*****************************************************************************
2  * render.c : SPU renderer
3  *****************************************************************************
4  * Copyright (C) 2000-2001 VideoLAN
5  * $Id: render.c,v 1.4 2002/11/06 21:48:24 gbazin Exp $
6  *
7  * Authors: Samuel Hocevar <sam@zoy.org>
8  *          Rudolf Cornelissen <rag.cornelissen@inter.nl.net>
9  *          Roine Gustafsson <roine@popstar.com>
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., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
24  *****************************************************************************/
25
26 /*****************************************************************************
27  * Preamble
28  *****************************************************************************/
29 #include <stdlib.h>                                      /* malloc(), free() */
30 #include <string.h>                                    /* memcpy(), memset() */
31
32 #include <vlc/vlc.h>
33 #include <vlc/vout.h>
34 #include <vlc/decoder.h>
35
36 #include "spudec.h"
37
38 /*****************************************************************************
39  * Local prototypes
40  *****************************************************************************/
41 static void RenderI420( vout_thread_t *, picture_t *, const subpicture_t *,
42                         vlc_bool_t );
43 static void RenderRV16( vout_thread_t *, picture_t *, const subpicture_t *,
44                         vlc_bool_t );
45 static void RenderRV32( vout_thread_t *, picture_t *, const subpicture_t *,
46                         vlc_bool_t );
47 static void RenderYUY2( vout_thread_t *, picture_t *, const subpicture_t *,
48                         vlc_bool_t );
49
50 /*****************************************************************************
51  * RenderSPU: draw an SPU on a picture
52  *****************************************************************************
53  * This is a fast implementation of the subpicture drawing code. The data
54  * has been preprocessed once, so we don't need to parse the RLE buffer again
55  * and again. Most sanity checks are already done so that this routine can be
56  * as fast as possible.
57  *****************************************************************************/
58 void E_(RenderSPU)( vout_thread_t *p_vout, picture_t *p_pic,
59                     const subpicture_t *p_spu )
60 {
61     switch( p_vout->output.i_chroma )
62     {
63         /* I420 target, no scaling */
64         case VLC_FOURCC('I','4','2','0'):
65         case VLC_FOURCC('I','Y','U','V'):
66         case VLC_FOURCC('Y','V','1','2'):
67             RenderI420( p_vout, p_pic, p_spu, p_spu->p_sys->b_crop );
68             break;
69
70         /* RV16 target, scaling */
71         case VLC_FOURCC('R','V','1','6'):
72             RenderRV16( p_vout, p_pic, p_spu, p_spu->p_sys->b_crop );
73             break;
74
75         /* RV32 target, scaling */
76         case VLC_FOURCC('R','V','2','4'):
77         case VLC_FOURCC('R','V','3','2'):
78             RenderRV32( p_vout, p_pic, p_spu, p_spu->p_sys->b_crop );
79             break;
80
81         /* NVidia overlay, no scaling */
82         case VLC_FOURCC('Y','U','Y','2'):
83             RenderYUY2( p_vout, p_pic, p_spu, p_spu->p_sys->b_crop );
84             break;
85
86         default:
87             msg_Err( p_vout, "unknown chroma, can't render SPU" );
88             break;
89     }
90 }
91
92 /* Following functions are local */
93
94 static void RenderI420( vout_thread_t *p_vout, picture_t *p_pic,
95                         const subpicture_t *p_spu, vlc_bool_t b_crop )
96 {
97     /* Common variables */
98     u8  *p_dest;
99     u8  *p_destptr;
100     u16 *p_source = (u16 *)p_spu->p_sys->p_data;
101
102     int i_x, i_y;
103     int i_len, i_color;
104     u16 i_colprecomp, i_destalpha;
105
106     /* Crop-specific */
107     int i_x_start, i_y_start, i_x_end, i_y_end;
108
109     p_dest = p_pic->Y_PIXELS + p_spu->i_x + p_spu->i_width
110               + p_pic->Y_PITCH * ( p_spu->i_y + p_spu->i_height );
111
112     i_x_start = p_spu->i_width - p_spu->p_sys->i_x_end;
113     i_y_start = p_pic->Y_PITCH * (p_spu->i_height - p_spu->p_sys->i_y_end );
114     i_x_end = p_spu->i_width - p_spu->p_sys->i_x_start;
115     i_y_end = p_pic->Y_PITCH * (p_spu->i_height - p_spu->p_sys->i_y_start );
116
117     /* Draw until we reach the bottom of the subtitle */
118     for( i_y = p_spu->i_height * p_pic->Y_PITCH ;
119          i_y ;
120          i_y -= p_pic->Y_PITCH )
121     {
122         /* Draw until we reach the end of the line */
123         for( i_x = p_spu->i_width ; i_x ; i_x -= i_len )
124         {
125             /* Get the RLE part, then draw the line */
126             i_color = *p_source & 0x3;
127             i_len = *p_source++ >> 2;
128
129             if( b_crop
130                  && ( i_x < i_x_start || i_x > i_x_end
131                        || i_y < i_y_start || i_y > i_y_end ) )
132             {
133                 continue;
134             }
135
136             switch( p_spu->p_sys->pi_alpha[i_color] )
137             {
138                 case 0x00:
139                     break;
140
141                 case 0x0f:
142                     memset( p_dest - i_x - i_y,
143                             p_spu->p_sys->pi_yuv[i_color][0], i_len );
144                     break;
145
146                 default:
147                     /* To be able to divide by 16 (>>4) we add 1 to the alpha.
148                      * This means Alpha 0 won't be completely transparent, but
149                      * that's handled in a special case above anyway. */
150                     i_colprecomp = (u16)p_spu->p_sys->pi_yuv[i_color][0]
151                                  * (u16)(p_spu->p_sys->pi_alpha[i_color] + 1);
152                     i_destalpha = 15 - p_spu->p_sys->pi_alpha[i_color];
153
154                     for ( p_destptr = p_dest - i_x - i_y;
155                           p_destptr < p_dest - i_x - i_y + i_len;
156                           p_destptr++ )
157                     {
158                         *p_destptr = ( i_colprecomp +
159                                         (u16)*p_destptr * i_destalpha ) >> 4;
160                     }
161                     break;
162             }
163         }
164     }
165 }
166
167 static void RenderRV16( vout_thread_t *p_vout, picture_t *p_pic,
168                         const subpicture_t *p_spu, vlc_bool_t b_crop )
169 {
170     /* Common variables */
171     u16  p_clut16[4];
172     u8  *p_dest;
173     u16 *p_source = (u16 *)p_spu->p_sys->p_data;
174
175     int i_x, i_y;
176     int i_len, i_color;
177
178     /* RGB-specific */
179     int i_xscale, i_yscale, i_width, i_height, i_ytmp, i_yreal, i_ynext;
180
181     /* Crop-specific */
182     int i_x_start, i_y_start, i_x_end, i_y_end;
183
184     /* XXX: this is a COMPLETE HACK, memcpy is unable to do u16s anyway */
185     /* FIXME: get this from the DVD */
186     for( i_color = 0; i_color < 4; i_color++ )
187     {
188         p_clut16[i_color] = 0x1111
189                              * ( (u16)p_spu->p_sys->pi_yuv[i_color][0] >> 4 );
190     }
191
192     i_xscale = ( p_vout->output.i_width << 6 ) / p_vout->render.i_width;
193     i_yscale = ( p_vout->output.i_height << 6 ) / p_vout->render.i_height;
194
195     i_width  = p_spu->i_width  * i_xscale;
196     i_height = p_spu->i_height * i_yscale;
197
198     p_dest = p_pic->p->p_pixels + ( i_width >> 6 ) * 2
199               /* Add the picture coordinates and the SPU coordinates */
200               + ( (p_spu->i_x * i_xscale) >> 6 ) * 2
201               + ( (p_spu->i_y * i_yscale) >> 6 ) * p_pic->p->i_pitch;
202
203     i_x_start = i_width - i_xscale * p_spu->p_sys->i_x_end;
204     i_y_start = i_yscale * p_spu->p_sys->i_y_start;
205     i_x_end = i_width - i_xscale * p_spu->p_sys->i_x_start;
206     i_y_end = i_yscale * p_spu->p_sys->i_y_end;
207
208     /* Draw until we reach the bottom of the subtitle */
209     for( i_y = 0 ; i_y < i_height ; )
210     {
211         i_ytmp = i_y >> 6;
212         i_y += i_yscale;
213
214         /* Check whether we need to draw one line or more than one */
215         if( i_ytmp + 1 >= ( i_y >> 6 ) )
216         {
217             /* Just one line : we precalculate i_y >> 6 */
218             i_yreal = p_pic->p->i_pitch * i_ytmp;
219
220             /* Draw until we reach the end of the line */
221             for( i_x = i_width ; i_x ; i_x -= i_len )
222             {
223                 /* Get the RLE part, then draw the line */
224                 i_color = *p_source & 0x3;
225                 i_len = i_xscale * ( *p_source++ >> 2 );
226
227                 if( b_crop
228                      && ( i_x < i_x_start || i_x > i_x_end
229                            || i_y < i_y_start || i_y > i_y_end ) )
230                 {
231                     continue;
232                 }
233
234                 switch( p_spu->p_sys->pi_alpha[ i_color ] )
235                 {
236                 case 0x00:
237                     break;
238
239                 case 0x0f:
240                     memset( p_dest - 2 * ( i_x >> 6 ) + i_yreal,
241                             p_clut16[ i_color ],
242                             2 * ( ( i_len >> 6 ) + 1 ) );
243                     break;
244
245                 default:
246                     /* FIXME: we should do transparency */
247                     memset( p_dest - 2 * ( i_x >> 6 ) + i_yreal,
248                             p_clut16[ i_color ],
249                             2 * ( ( i_len >> 6 ) + 1 ) );
250                     break;
251                 }
252             }
253         }
254         else
255         {
256             i_yreal = p_pic->p->i_pitch * i_ytmp;
257             i_ynext = p_pic->p->i_pitch * i_y >> 6;
258
259             /* Draw until we reach the end of the line */
260             for( i_x = i_width ; i_x ; i_x -= i_len )
261             {
262                 /* Get the RLE part, then draw as many lines as needed */
263                 i_color = *p_source & 0x3;
264                 i_len = i_xscale * ( *p_source++ >> 2 );
265
266                 if( b_crop
267                      && ( i_x < i_x_start || i_x > i_x_end
268                            || i_y < i_y_start || i_y > i_y_end ) )
269                 {
270                     continue;
271                 }
272
273                 switch( p_spu->p_sys->pi_alpha[ i_color ] )
274                 {
275                 case 0x00:
276                     break;
277
278                 case 0x0f:
279                     for( i_ytmp = i_yreal ; i_ytmp < i_ynext ;
280                          i_ytmp += p_pic->p->i_pitch )
281                     {
282                         memset( p_dest - 2 * ( i_x >> 6 ) + i_ytmp,
283                                 p_clut16[ i_color ],
284                                 2 * ( ( i_len >> 6 ) + 1 ) );
285                     }
286                     break;
287
288                 default:
289                     /* FIXME: we should do transparency */
290                     for( i_ytmp = i_yreal ; i_ytmp < i_ynext ;
291                          i_ytmp += p_pic->p->i_pitch )
292                     {
293                         memset( p_dest - 2 * ( i_x >> 6 ) + i_ytmp,
294                                 p_clut16[ i_color ],
295                                 2 * ( ( i_len >> 6 ) + 1 ) );
296                     }
297                     break;
298                 }
299             }
300         }
301     }
302 }
303
304 static void RenderRV32( vout_thread_t *p_vout, picture_t *p_pic,
305                         const subpicture_t *p_spu, vlc_bool_t b_crop )
306 {
307     /* Common variables */
308     u32  p_clut32[4];
309     u8  *p_dest;
310     u16 *p_source = (u16 *)p_spu->p_sys->p_data;
311
312     int i_x, i_y;
313     int i_len, i_color;
314
315     /* RGB-specific */
316     int i_xscale, i_yscale, i_width, i_height, i_ytmp, i_yreal, i_ynext;
317
318     /* Crop-specific */
319     int i_x_start, i_y_start, i_x_end, i_y_end;
320
321     /* XXX: this is a COMPLETE HACK, memcpy is unable to do u32s anyway */
322     /* FIXME: get this from the DVD */
323     for( i_color = 0; i_color < 4; i_color++ )
324     {
325         p_clut32[i_color] = 0x11111111
326                              * ( (u16)p_spu->p_sys->pi_yuv[i_color][0] >> 4 );
327     }
328
329     i_xscale = ( p_vout->output.i_width << 6 ) / p_vout->render.i_width;
330     i_yscale = ( p_vout->output.i_height << 6 ) / p_vout->render.i_height;
331
332     i_width  = p_spu->i_width  * i_xscale;
333     i_height = p_spu->i_height * i_yscale;
334
335     i_x_start = i_width - i_xscale * p_spu->p_sys->i_x_end;
336     i_y_start = i_yscale * p_spu->p_sys->i_y_start;
337     i_x_end = i_width - i_xscale * p_spu->p_sys->i_x_start;
338     i_y_end = i_yscale * p_spu->p_sys->i_y_end;
339
340     p_dest = p_pic->p->p_pixels + ( i_width >> 6 ) * 4
341               /* Add the picture coordinates and the SPU coordinates */
342               + ( (p_spu->i_x * i_xscale) >> 6 ) * 4
343               + ( (p_spu->i_y * i_yscale) >> 6 ) * p_pic->p->i_pitch;
344
345     /* Draw until we reach the bottom of the subtitle */
346     for( i_y = 0 ; i_y < i_height ; )
347     {
348         i_ytmp = i_y >> 6;
349         i_y += i_yscale;
350
351         /* Check whether we need to draw one line or more than one */
352         if( i_ytmp + 1 >= ( i_y >> 6 ) )
353         {
354             /* Just one line : we precalculate i_y >> 6 */
355             i_yreal = p_pic->p->i_pitch * i_ytmp;
356
357             /* Draw until we reach the end of the line */
358             for( i_x = i_width ; i_x ; i_x -= i_len )
359             {
360                 /* Get the RLE part, then draw the line */
361                 i_color = *p_source & 0x3;
362                 i_len = i_xscale * ( *p_source++ >> 2 );
363
364                 if( b_crop
365                      && ( i_x < i_x_start || i_x > i_x_end
366                            || i_y < i_y_start || i_y > i_y_end ) )
367                 {
368                     continue;
369                 }
370
371                 switch( p_spu->p_sys->pi_alpha[ i_color ] )
372                 {
373                 case 0x00:
374                     break;
375
376                 case 0x0f:
377                     memset( p_dest - 4 * ( i_x >> 6 ) + i_yreal,
378                             p_clut32[ i_color ], 4 * ( ( i_len >> 6 ) + 1 ) );
379                     break;
380
381                 default:
382                     /* FIXME: we should do transparency */
383                     memset( p_dest - 4 * ( i_x >> 6 ) + i_yreal,
384                             p_clut32[ i_color ], 4 * ( ( i_len >> 6 ) + 1 ) );
385                     break;
386                 }
387             }
388         }
389         else
390         {
391             i_yreal = p_pic->p->i_pitch * i_ytmp;
392             i_ynext = p_pic->p->i_pitch * i_y >> 6;
393
394             /* Draw until we reach the end of the line */
395             for( i_x = i_width ; i_x ; i_x -= i_len )
396             {
397                 /* Get the RLE part, then draw as many lines as needed */
398                 i_color = *p_source & 0x3;
399                 i_len = i_xscale * ( *p_source++ >> 2 );
400
401                 if( b_crop
402                      && ( i_x < i_x_start || i_x > i_x_end
403                            || i_y < i_y_start || i_y > i_y_end ) )
404                 {
405                     continue;
406                 }
407
408                 switch( p_spu->p_sys->pi_alpha[ i_color ] )
409                 {
410                 case 0x00:
411                     break;
412
413                 case 0x0f:
414                     for( i_ytmp = i_yreal ; i_ytmp < i_ynext ;
415                          i_ytmp += p_pic->p->i_pitch )
416                     {
417                         memset( p_dest - 4 * ( i_x >> 6 ) + i_ytmp,
418                                 p_clut32[ i_color ],
419                                 4 * ( ( i_len >> 6 ) + 1 ) );
420                     }
421                     break;
422
423                 default:
424                     /* FIXME: we should do transparency */
425                     for( i_ytmp = i_yreal ; i_ytmp < i_ynext ;
426                          i_ytmp += p_pic->p->i_pitch )
427                     {
428                         memset( p_dest - 4 * ( i_x >> 6 ) + i_ytmp,
429                                 p_clut32[ i_color ],
430                                 4 * ( ( i_len >> 6 ) + 1 ) );
431                     }
432                     break;
433                 }
434             }
435         }
436     }
437 }
438
439 static void RenderYUY2( vout_thread_t *p_vout, picture_t *p_pic,
440                         const subpicture_t *p_spu, vlc_bool_t b_crop )
441 {
442     /* Common variables */
443     u8  *p_dest;
444     u16 *p_source = (u16 *)p_spu->p_sys->p_data;
445
446     int i_x, i_y;
447     int i_len, i_color;
448     u8  i_cnt;
449
450     /* Crop-specific */
451     int i_x_start, i_y_start, i_x_end, i_y_end;
452
453     p_dest = p_pic->p->p_pixels +
454               + ( p_spu->i_y + p_spu->i_height ) * p_pic->p->i_pitch  // * bytes per line
455               + ( p_spu->i_x + p_spu->i_width ) * 2;  // * bytes per pixel
456
457     i_x_start = p_spu->i_width - p_spu->p_sys->i_x_end;
458     i_y_start = (p_spu->i_height - p_spu->p_sys->i_y_end)
459                   * p_pic->p->i_pitch / 2;
460     i_x_end = p_spu->i_width - p_spu->p_sys->i_x_start;
461     i_y_end = (p_spu->i_height - p_spu->p_sys->i_y_start)
462                 * p_pic->p->i_pitch / 2;
463
464     /* Draw until we reach the bottom of the subtitle */
465     for( i_y = p_spu->i_height * p_pic->p->i_pitch / 2;
466          i_y ;
467          i_y -= p_pic->p->i_pitch / 2 )
468     {
469         /* Draw until we reach the end of the line */
470         for( i_x = p_spu->i_width ; i_x ; i_x -= i_len )
471         {
472             /* Get the RLE part, then draw the line */
473             i_color = *p_source & 0x3;
474             i_len = *p_source++ >> 2;
475
476             if( b_crop
477                  && ( i_x < i_x_start || i_x > i_x_end
478                        || i_y < i_y_start || i_y > i_y_end ) )
479             {
480                 continue;
481             }
482
483             switch( p_spu->p_sys->pi_alpha[ i_color ] )
484             {
485             case 0x00:
486                 break;
487
488             case 0x0f:
489                 for( i_cnt = 0; i_cnt < i_len; i_cnt++ )
490                 {
491                     /* draw a pixel */
492                     /* Y */
493                     memset( p_dest - i_x * 2 - i_y * 2 + i_cnt * 2,
494                             p_spu->p_sys->pi_yuv[i_color][0], 1);
495
496                     if (!(i_cnt & 0x01))
497                     {
498                         /* U and V */
499                         memset( p_dest - i_x * 2 - i_y * 2 + i_cnt * 2 + 1,
500                                 0x80, 1);
501                         memset( p_dest - i_x * 2 - i_y * 2 + i_cnt * 2 + 3,
502                                 0x80, 1);
503                     }
504                 }
505                 break;
506
507             default:
508                 /* FIXME: we should do transparency */
509                 for( i_cnt = 0; i_cnt < i_len; i_cnt++ )
510                 {
511                     /* draw a pixel */
512                     /* Y */
513                     memset( p_dest - i_x * 2 - i_y * 2 + i_cnt * 2,
514                             p_spu->p_sys->pi_yuv[i_color][0], 1);
515
516                     if (!(i_cnt & 0x01))
517                     {
518                         /* U and V */
519                         memset( p_dest - i_x * 2 - i_y * 2 + i_cnt * 2 + 1,
520                                 0x80, 1);
521                         memset( p_dest - i_x * 2 - i_y * 2 + i_cnt * 2 + 3,
522                                 0x80, 1);
523                     }
524                 }
525                 break;
526             }
527         }
528     }
529 }
530