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