1 /*****************************************************************************
2 * render.c : SPU renderer
3 *****************************************************************************
4 * Copyright (C) 2000-2001 VideoLAN
5 * $Id: render.c,v 1.5 2003/07/22 20:49:10 hartman Exp $
7 * Authors: Samuel Hocevar <sam@zoy.org>
8 * Rudolf Cornelissen <rag.cornelissen@inter.nl.net>
9 * Roine Gustafsson <roine@popstar.com>
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.
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.
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 *****************************************************************************/
26 /*****************************************************************************
28 *****************************************************************************/
29 #include <stdlib.h> /* malloc(), free() */
30 #include <string.h> /* memcpy(), memset() */
34 #include <vlc/decoder.h>
38 /*****************************************************************************
40 *****************************************************************************/
41 static void RenderI420( vout_thread_t *, picture_t *, const subpicture_t *,
43 static void RenderRV16( vout_thread_t *, picture_t *, const subpicture_t *,
45 static void RenderRV32( vout_thread_t *, picture_t *, const subpicture_t *,
47 static void RenderYUY2( vout_thread_t *, picture_t *, const subpicture_t *,
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 )
61 switch( p_vout->output.i_chroma )
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 );
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 );
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 );
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 );
87 msg_Err( p_vout, "unknown chroma, can't render SPU" );
92 /* Following functions are local */
94 static void RenderI420( vout_thread_t *p_vout, picture_t *p_pic,
95 const subpicture_t *p_spu, vlc_bool_t b_crop )
97 /* Common variables */
100 u16 *p_source = (u16 *)p_spu->p_sys->p_data;
104 u16 i_colprecomp, i_destalpha;
107 int i_x_start, i_y_start, i_x_end, i_y_end;
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 );
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 );
117 /* Draw until we reach the bottom of the subtitle */
118 for( i_y = p_spu->i_height * p_pic->Y_PITCH ;
120 i_y -= p_pic->Y_PITCH )
122 /* Draw until we reach the end of the line */
123 for( i_x = p_spu->i_width ; i_x ; i_x -= i_len )
125 /* Get the RLE part, then draw the line */
126 i_color = *p_source & 0x3;
127 i_len = *p_source++ >> 2;
130 && ( i_x < i_x_start || i_x > i_x_end
131 || i_y < i_y_start || i_y > i_y_end ) )
136 switch( p_spu->p_sys->pi_alpha[i_color] )
142 memset( p_dest - i_x - i_y,
143 p_spu->p_sys->pi_yuv[i_color][0], i_len );
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];
154 for ( p_destptr = p_dest - i_x - i_y;
155 p_destptr < p_dest - i_x - i_y + i_len;
158 *p_destptr = ( i_colprecomp +
159 (u16)*p_destptr * i_destalpha ) >> 4;
167 static void RenderRV16( vout_thread_t *p_vout, picture_t *p_pic,
168 const subpicture_t *p_spu, vlc_bool_t b_crop )
170 /* Common variables */
173 u16 *p_source = (u16 *)p_spu->p_sys->p_data;
179 int i_xscale, i_yscale, i_width, i_height, i_ytmp, i_yreal, i_ynext;
182 int i_x_start, i_y_start, i_x_end, i_y_end;
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++ )
188 p_clut16[i_color] = 0x1111
189 * ( (u16)p_spu->p_sys->pi_yuv[i_color][0] >> 4 );
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;
195 i_width = p_spu->i_width * i_xscale;
196 i_height = p_spu->i_height * i_yscale;
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;
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;
208 /* Draw until we reach the bottom of the subtitle */
209 for( i_y = 0 ; i_y < i_height ; )
214 /* Check whether we need to draw one line or more than one */
215 if( i_ytmp + 1 >= ( i_y >> 6 ) )
217 /* Just one line : we precalculate i_y >> 6 */
218 i_yreal = p_pic->p->i_pitch * i_ytmp;
220 /* Draw until we reach the end of the line */
221 for( i_x = i_width ; i_x ; i_x -= i_len )
223 /* Get the RLE part, then draw the line */
224 i_color = *p_source & 0x3;
225 i_len = i_xscale * ( *p_source++ >> 2 );
228 && ( i_x < i_x_start || i_x > i_x_end
229 || i_y < i_y_start || i_y > i_y_end ) )
234 switch( p_spu->p_sys->pi_alpha[ i_color ] )
240 memset( p_dest - 2 * ( i_x >> 6 ) + i_yreal,
242 2 * ( ( i_len >> 6 ) + 1 ) );
246 /* FIXME: we should do transparency */
247 memset( p_dest - 2 * ( i_x >> 6 ) + i_yreal,
249 2 * ( ( i_len >> 6 ) + 1 ) );
256 i_yreal = p_pic->p->i_pitch * i_ytmp;
257 i_ynext = p_pic->p->i_pitch * i_y >> 6;
259 /* Draw until we reach the end of the line */
260 for( i_x = i_width ; i_x ; i_x -= i_len )
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 );
267 && ( i_x < i_x_start || i_x > i_x_end
268 || i_y < i_y_start || i_y > i_y_end ) )
273 switch( p_spu->p_sys->pi_alpha[ i_color ] )
279 for( i_ytmp = i_yreal ; i_ytmp < i_ynext ;
280 i_ytmp += p_pic->p->i_pitch )
282 memset( p_dest - 2 * ( i_x >> 6 ) + i_ytmp,
284 2 * ( ( i_len >> 6 ) + 1 ) );
289 /* FIXME: we should do transparency */
290 for( i_ytmp = i_yreal ; i_ytmp < i_ynext ;
291 i_ytmp += p_pic->p->i_pitch )
293 memset( p_dest - 2 * ( i_x >> 6 ) + i_ytmp,
295 2 * ( ( i_len >> 6 ) + 1 ) );
304 static void RenderRV32( vout_thread_t *p_vout, picture_t *p_pic,
305 const subpicture_t *p_spu, vlc_bool_t b_crop )
307 /* Common variables */
310 u16 *p_source = (u16 *)p_spu->p_sys->p_data;
316 int i_xscale, i_yscale, i_width, i_height, i_ytmp, i_yreal, i_ynext;
319 int i_x_start, i_y_start, i_x_end, i_y_end;
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++ )
325 p_clut32[i_color] = 0x11111111
326 * ( (u16)p_spu->p_sys->pi_yuv[i_color][0] >> 4 );
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;
332 i_width = p_spu->i_width * i_xscale;
333 i_height = p_spu->i_height * i_yscale;
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;
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;
345 /* Draw until we reach the bottom of the subtitle */
346 for( i_y = 0 ; i_y < i_height ; )
351 /* Check whether we need to draw one line or more than one */
352 if( i_ytmp + 1 >= ( i_y >> 6 ) )
354 /* Just one line : we precalculate i_y >> 6 */
355 i_yreal = p_pic->p->i_pitch * i_ytmp;
357 /* Draw until we reach the end of the line */
358 for( i_x = i_width ; i_x ; i_x -= i_len )
360 /* Get the RLE part, then draw the line */
361 i_color = *p_source & 0x3;
362 i_len = i_xscale * ( *p_source++ >> 2 );
365 && ( i_x < i_x_start || i_x > i_x_end
366 || i_y < i_y_start || i_y > i_y_end ) )
371 switch( p_spu->p_sys->pi_alpha[ i_color ] )
377 memset( p_dest - 4 * ( i_x >> 6 ) + i_yreal,
378 p_clut32[ i_color ], 4 * ( ( i_len >> 6 ) + 1 ) );
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 ) );
391 i_yreal = p_pic->p->i_pitch * i_ytmp;
392 i_ynext = p_pic->p->i_pitch * i_y >> 6;
394 /* Draw until we reach the end of the line */
395 for( i_x = i_width ; i_x ; i_x -= i_len )
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 );
402 && ( i_x < i_x_start || i_x > i_x_end
403 || i_y < i_y_start || i_y > i_y_end ) )
408 switch( p_spu->p_sys->pi_alpha[ i_color ] )
414 for( i_ytmp = i_yreal ; i_ytmp < i_ynext ;
415 i_ytmp += p_pic->p->i_pitch )
417 memset( p_dest - 4 * ( i_x >> 6 ) + i_ytmp,
419 4 * ( ( i_len >> 6 ) + 1 ) );
424 /* FIXME: we should do transparency */
425 for( i_ytmp = i_yreal ; i_ytmp < i_ynext ;
426 i_ytmp += p_pic->p->i_pitch )
428 memset( p_dest - 4 * ( i_x >> 6 ) + i_ytmp,
430 4 * ( ( i_len >> 6 ) + 1 ) );
439 static void RenderYUY2( vout_thread_t *p_vout, picture_t *p_pic,
440 const subpicture_t *p_spu, vlc_bool_t b_crop )
442 /* Common variables */
444 u16 *p_source = (u16 *)p_spu->p_sys->p_data;
451 int i_x_start, i_y_start, i_x_end, i_y_end;
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
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;
464 /* Draw until we reach the bottom of the subtitle */
465 for( i_y = p_spu->i_height * p_pic->p->i_pitch / 2;
467 i_y -= p_pic->p->i_pitch / 2 )
469 /* Draw until we reach the end of the line */
470 for( i_x = p_spu->i_width ; i_x ; i_x -= i_len )
472 /* Get the RLE part, then draw the line */
473 i_color = *p_source & 0x3;
474 i_len = *p_source++ >> 2;
477 && ( i_x < i_x_start || i_x > i_x_end
478 || i_y < i_y_start || i_y > i_y_end ) )
483 switch( p_spu->p_sys->pi_alpha[ i_color ] )
489 for( i_cnt = 0; i_cnt < i_len; i_cnt++ )
493 memset( p_dest - i_x * 2 - i_y * 2 + i_cnt * 2,
494 p_spu->p_sys->pi_yuv[i_color][0], 1);
499 memset( p_dest - i_x * 2 - i_y * 2 + i_cnt * 2 + 1,
501 memset( p_dest - i_x * 2 - i_y * 2 + i_cnt * 2 + 3,
508 /* FIXME: we should do transparency */
509 for( i_cnt = 0; i_cnt < i_len; i_cnt++ )
513 memset( p_dest - i_x * 2 - i_y * 2 + i_cnt * 2,
514 p_spu->p_sys->pi_yuv[i_color][0], 1);
519 memset( p_dest - i_x * 2 - i_y * 2 + i_cnt * 2 + 1,
521 memset( p_dest - i_x * 2 - i_y * 2 + i_cnt * 2 + 3,