1 /*****************************************************************************
2 * render.c : SPU renderer
3 *****************************************************************************
4 * Copyright (C) 2000-2001 VideoLAN
5 * $Id: render.c,v 1.7 2003/11/22 19:55:47 fenrir 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() */
33 #include <vlc/decoder.h>
37 /*****************************************************************************
39 *****************************************************************************/
40 static void RenderI420( vout_thread_t *, picture_t *, const subpicture_t *,
42 static void RenderRV16( vout_thread_t *, picture_t *, const subpicture_t *,
44 static void RenderRV32( vout_thread_t *, picture_t *, const subpicture_t *,
46 static void RenderYUY2( vout_thread_t *, picture_t *, const subpicture_t *,
49 /*****************************************************************************
50 * RenderSPU: draw an SPU on a picture
51 *****************************************************************************
52 * This is a fast implementation of the subpicture drawing code. The data
53 * has been preprocessed once, so we don't need to parse the RLE buffer again
54 * and again. Most sanity checks are already done so that this routine can be
55 * as fast as possible.
56 *****************************************************************************/
57 void E_(RenderSPU)( vout_thread_t *p_vout, picture_t *p_pic,
58 const subpicture_t *p_spu )
60 switch( p_vout->output.i_chroma )
62 /* I420 target, no scaling */
63 case VLC_FOURCC('I','4','2','0'):
64 case VLC_FOURCC('I','Y','U','V'):
65 case VLC_FOURCC('Y','V','1','2'):
66 RenderI420( p_vout, p_pic, p_spu, p_spu->p_sys->b_crop );
69 /* RV16 target, scaling */
70 case VLC_FOURCC('R','V','1','6'):
71 RenderRV16( p_vout, p_pic, p_spu, p_spu->p_sys->b_crop );
74 /* RV32 target, scaling */
75 case VLC_FOURCC('R','V','2','4'):
76 case VLC_FOURCC('R','V','3','2'):
77 RenderRV32( p_vout, p_pic, p_spu, p_spu->p_sys->b_crop );
80 /* NVidia overlay, no scaling */
81 case VLC_FOURCC('Y','U','Y','2'):
82 RenderYUY2( p_vout, p_pic, p_spu, p_spu->p_sys->b_crop );
86 msg_Err( p_vout, "unknown chroma, can't render SPU" );
91 /* Following functions are local */
93 static void RenderI420( vout_thread_t *p_vout, picture_t *p_pic,
94 const subpicture_t *p_spu, vlc_bool_t b_crop )
96 /* Common variables */
99 uint16_t *p_source = (uint16_t *)p_spu->p_sys->p_data;
103 uint16_t i_colprecomp, i_destalpha;
106 int i_x_start, i_y_start, i_x_end, i_y_end;
108 p_dest = p_pic->Y_PIXELS + p_spu->i_x + p_spu->i_width
109 + p_pic->Y_PITCH * ( p_spu->i_y + p_spu->i_height );
111 i_x_start = p_spu->i_width - p_spu->p_sys->i_x_end;
112 i_y_start = p_pic->Y_PITCH * (p_spu->i_height - p_spu->p_sys->i_y_end );
113 i_x_end = p_spu->i_width - p_spu->p_sys->i_x_start;
114 i_y_end = p_pic->Y_PITCH * (p_spu->i_height - p_spu->p_sys->i_y_start );
116 /* Draw until we reach the bottom of the subtitle */
117 for( i_y = p_spu->i_height * p_pic->Y_PITCH ;
119 i_y -= p_pic->Y_PITCH )
121 /* Draw until we reach the end of the line */
122 for( i_x = p_spu->i_width ; i_x ; i_x -= i_len )
124 /* Get the RLE part, then draw the line */
125 i_color = *p_source & 0x3;
126 i_len = *p_source++ >> 2;
129 && ( i_x < i_x_start || i_x > i_x_end
130 || i_y < i_y_start || i_y > i_y_end ) )
135 switch( p_spu->p_sys->pi_alpha[i_color] )
141 memset( p_dest - i_x - i_y,
142 p_spu->p_sys->pi_yuv[i_color][0], i_len );
146 /* To be able to divide by 16 (>>4) we add 1 to the alpha.
147 * This means Alpha 0 won't be completely transparent, but
148 * that's handled in a special case above anyway. */
149 i_colprecomp = (uint16_t)p_spu->p_sys->pi_yuv[i_color][0]
150 * (uint16_t)(p_spu->p_sys->pi_alpha[i_color] + 1);
151 i_destalpha = 15 - p_spu->p_sys->pi_alpha[i_color];
153 for ( p_destptr = p_dest - i_x - i_y;
154 p_destptr < p_dest - i_x - i_y + i_len;
157 *p_destptr = ( i_colprecomp +
158 (uint16_t)*p_destptr * i_destalpha ) >> 4;
166 static void RenderRV16( vout_thread_t *p_vout, picture_t *p_pic,
167 const subpicture_t *p_spu, vlc_bool_t b_crop )
169 /* Common variables */
170 uint16_t p_clut16[4];
172 uint16_t *p_source = (uint16_t *)p_spu->p_sys->p_data;
178 int i_xscale, i_yscale, i_width, i_height, i_ytmp, i_yreal, i_ynext;
181 int i_x_start, i_y_start, i_x_end, i_y_end;
183 /* XXX: this is a COMPLETE HACK, memcpy is unable to do u16s anyway */
184 /* FIXME: get this from the DVD */
185 for( i_color = 0; i_color < 4; i_color++ )
187 p_clut16[i_color] = 0x1111
188 * ( (uint16_t)p_spu->p_sys->pi_yuv[i_color][0] >> 4 );
191 i_xscale = ( p_vout->output.i_width << 6 ) / p_vout->render.i_width;
192 i_yscale = ( p_vout->output.i_height << 6 ) / p_vout->render.i_height;
194 i_width = p_spu->i_width * i_xscale;
195 i_height = p_spu->i_height * i_yscale;
197 p_dest = p_pic->p->p_pixels + ( i_width >> 6 ) * 2
198 /* Add the picture coordinates and the SPU coordinates */
199 + ( (p_spu->i_x * i_xscale) >> 6 ) * 2
200 + ( (p_spu->i_y * i_yscale) >> 6 ) * p_pic->p->i_pitch;
202 i_x_start = i_width - i_xscale * p_spu->p_sys->i_x_end;
203 i_y_start = i_yscale * p_spu->p_sys->i_y_start;
204 i_x_end = i_width - i_xscale * p_spu->p_sys->i_x_start;
205 i_y_end = i_yscale * p_spu->p_sys->i_y_end;
207 /* Draw until we reach the bottom of the subtitle */
208 for( i_y = 0 ; i_y < i_height ; )
213 /* Check whether we need to draw one line or more than one */
214 if( i_ytmp + 1 >= ( i_y >> 6 ) )
216 /* Just one line : we precalculate i_y >> 6 */
217 i_yreal = p_pic->p->i_pitch * i_ytmp;
219 /* Draw until we reach the end of the line */
220 for( i_x = i_width ; i_x ; i_x -= i_len )
222 /* Get the RLE part, then draw the line */
223 i_color = *p_source & 0x3;
224 i_len = i_xscale * ( *p_source++ >> 2 );
227 && ( i_x < i_x_start || i_x > i_x_end
228 || i_y < i_y_start || i_y > i_y_end ) )
233 switch( p_spu->p_sys->pi_alpha[ i_color ] )
239 memset( p_dest - 2 * ( i_x >> 6 ) + i_yreal,
241 2 * ( ( i_len >> 6 ) + 1 ) );
245 /* FIXME: we should do transparency */
246 memset( p_dest - 2 * ( i_x >> 6 ) + i_yreal,
248 2 * ( ( i_len >> 6 ) + 1 ) );
255 i_yreal = p_pic->p->i_pitch * i_ytmp;
256 i_ynext = p_pic->p->i_pitch * i_y >> 6;
258 /* Draw until we reach the end of the line */
259 for( i_x = i_width ; i_x ; i_x -= i_len )
261 /* Get the RLE part, then draw as many lines as needed */
262 i_color = *p_source & 0x3;
263 i_len = i_xscale * ( *p_source++ >> 2 );
266 && ( i_x < i_x_start || i_x > i_x_end
267 || i_y < i_y_start || i_y > i_y_end ) )
272 switch( p_spu->p_sys->pi_alpha[ i_color ] )
278 for( i_ytmp = i_yreal ; i_ytmp < i_ynext ;
279 i_ytmp += p_pic->p->i_pitch )
281 memset( p_dest - 2 * ( i_x >> 6 ) + i_ytmp,
283 2 * ( ( i_len >> 6 ) + 1 ) );
288 /* FIXME: we should do transparency */
289 for( i_ytmp = i_yreal ; i_ytmp < i_ynext ;
290 i_ytmp += p_pic->p->i_pitch )
292 memset( p_dest - 2 * ( i_x >> 6 ) + i_ytmp,
294 2 * ( ( i_len >> 6 ) + 1 ) );
303 static void RenderRV32( vout_thread_t *p_vout, picture_t *p_pic,
304 const subpicture_t *p_spu, vlc_bool_t b_crop )
306 /* Common variables */
307 uint32_t p_clut32[4];
309 uint16_t *p_source = (uint16_t *)p_spu->p_sys->p_data;
315 int i_xscale, i_yscale, i_width, i_height, i_ytmp, i_yreal, i_ynext;
318 int i_x_start, i_y_start, i_x_end, i_y_end;
320 /* XXX: this is a COMPLETE HACK, memcpy is unable to do u32s anyway */
321 /* FIXME: get this from the DVD */
322 for( i_color = 0; i_color < 4; i_color++ )
324 p_clut32[i_color] = 0x11111111
325 * ( (uint16_t)p_spu->p_sys->pi_yuv[i_color][0] >> 4 );
328 i_xscale = ( p_vout->output.i_width << 6 ) / p_vout->render.i_width;
329 i_yscale = ( p_vout->output.i_height << 6 ) / p_vout->render.i_height;
331 i_width = p_spu->i_width * i_xscale;
332 i_height = p_spu->i_height * i_yscale;
334 i_x_start = i_width - i_xscale * p_spu->p_sys->i_x_end;
335 i_y_start = i_yscale * p_spu->p_sys->i_y_start;
336 i_x_end = i_width - i_xscale * p_spu->p_sys->i_x_start;
337 i_y_end = i_yscale * p_spu->p_sys->i_y_end;
339 p_dest = p_pic->p->p_pixels + ( i_width >> 6 ) * 4
340 /* Add the picture coordinates and the SPU coordinates */
341 + ( (p_spu->i_x * i_xscale) >> 6 ) * 4
342 + ( (p_spu->i_y * i_yscale) >> 6 ) * p_pic->p->i_pitch;
344 /* Draw until we reach the bottom of the subtitle */
345 for( i_y = 0 ; i_y < i_height ; )
350 /* Check whether we need to draw one line or more than one */
351 if( i_ytmp + 1 >= ( i_y >> 6 ) )
353 /* Just one line : we precalculate i_y >> 6 */
354 i_yreal = p_pic->p->i_pitch * i_ytmp;
356 /* Draw until we reach the end of the line */
357 for( i_x = i_width ; i_x ; i_x -= i_len )
359 /* Get the RLE part, then draw the line */
360 i_color = *p_source & 0x3;
361 i_len = i_xscale * ( *p_source++ >> 2 );
364 && ( i_x < i_x_start || i_x > i_x_end
365 || i_y < i_y_start || i_y > i_y_end ) )
370 switch( p_spu->p_sys->pi_alpha[ i_color ] )
376 memset( p_dest - 4 * ( i_x >> 6 ) + i_yreal,
377 p_clut32[ i_color ], 4 * ( ( i_len >> 6 ) + 1 ) );
381 /* FIXME: we should do transparency */
382 memset( p_dest - 4 * ( i_x >> 6 ) + i_yreal,
383 p_clut32[ i_color ], 4 * ( ( i_len >> 6 ) + 1 ) );
390 i_yreal = p_pic->p->i_pitch * i_ytmp;
391 i_ynext = p_pic->p->i_pitch * i_y >> 6;
393 /* Draw until we reach the end of the line */
394 for( i_x = i_width ; i_x ; i_x -= i_len )
396 /* Get the RLE part, then draw as many lines as needed */
397 i_color = *p_source & 0x3;
398 i_len = i_xscale * ( *p_source++ >> 2 );
401 && ( i_x < i_x_start || i_x > i_x_end
402 || i_y < i_y_start || i_y > i_y_end ) )
407 switch( p_spu->p_sys->pi_alpha[ i_color ] )
413 for( i_ytmp = i_yreal ; i_ytmp < i_ynext ;
414 i_ytmp += p_pic->p->i_pitch )
416 memset( p_dest - 4 * ( i_x >> 6 ) + i_ytmp,
418 4 * ( ( i_len >> 6 ) + 1 ) );
423 /* FIXME: we should do transparency */
424 for( i_ytmp = i_yreal ; i_ytmp < i_ynext ;
425 i_ytmp += p_pic->p->i_pitch )
427 memset( p_dest - 4 * ( i_x >> 6 ) + i_ytmp,
429 4 * ( ( i_len >> 6 ) + 1 ) );
438 static void RenderYUY2( vout_thread_t *p_vout, picture_t *p_pic,
439 const subpicture_t *p_spu, vlc_bool_t b_crop )
441 /* Common variables */
443 uint16_t *p_source = (uint16_t *)p_spu->p_sys->p_data;
450 int i_x_start, i_y_start, i_x_end, i_y_end;
452 p_dest = p_pic->p->p_pixels +
453 + ( p_spu->i_y + p_spu->i_height ) * p_pic->p->i_pitch // * bytes per line
454 + ( p_spu->i_x + p_spu->i_width ) * 2; // * bytes per pixel
456 i_x_start = p_spu->i_width - p_spu->p_sys->i_x_end;
457 i_y_start = (p_spu->i_height - p_spu->p_sys->i_y_end)
458 * p_pic->p->i_pitch / 2;
459 i_x_end = p_spu->i_width - p_spu->p_sys->i_x_start;
460 i_y_end = (p_spu->i_height - p_spu->p_sys->i_y_start)
461 * p_pic->p->i_pitch / 2;
463 /* Draw until we reach the bottom of the subtitle */
464 for( i_y = p_spu->i_height * p_pic->p->i_pitch / 2;
466 i_y -= p_pic->p->i_pitch / 2 )
468 /* Draw until we reach the end of the line */
469 for( i_x = p_spu->i_width ; i_x ; i_x -= i_len )
471 /* Get the RLE part, then draw the line */
472 i_color = *p_source & 0x3;
473 i_len = *p_source++ >> 2;
476 && ( i_x < i_x_start || i_x > i_x_end
477 || i_y < i_y_start || i_y > i_y_end ) )
482 switch( p_spu->p_sys->pi_alpha[ i_color ] )
488 for( i_cnt = 0; i_cnt < i_len; i_cnt++ )
492 memset( p_dest - i_x * 2 - i_y * 2 + i_cnt * 2,
493 p_spu->p_sys->pi_yuv[i_color][0], 1);
498 memset( p_dest - i_x * 2 - i_y * 2 + i_cnt * 2 + 1,
500 memset( p_dest - i_x * 2 - i_y * 2 + i_cnt * 2 + 3,
507 /* FIXME: we should do transparency */
508 for( i_cnt = 0; i_cnt < i_len; i_cnt++ )
512 memset( p_dest - i_x * 2 - i_y * 2 + i_cnt * 2,
513 p_spu->p_sys->pi_yuv[i_color][0], 1);
518 memset( p_dest - i_x * 2 - i_y * 2 + i_cnt * 2 + 1,
520 memset( p_dest - i_x * 2 - i_y * 2 + i_cnt * 2 + 3,