]> git.sesse.net Git - vlc/blob - modules/codec/spudec/render.c
* all: ported to new API.
[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.7 2003/11/22 19:55:47 fenrir 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
31 #include <vlc/vlc.h>
32 #include <vlc/vout.h>
33 #include <vlc/decoder.h>
34
35 #include "spudec.h"
36
37 /*****************************************************************************
38  * Local prototypes
39  *****************************************************************************/
40 static void RenderI420( vout_thread_t *, picture_t *, const subpicture_t *,
41                         vlc_bool_t );
42 static void RenderRV16( vout_thread_t *, picture_t *, const subpicture_t *,
43                         vlc_bool_t );
44 static void RenderRV32( vout_thread_t *, picture_t *, const subpicture_t *,
45                         vlc_bool_t );
46 static void RenderYUY2( vout_thread_t *, picture_t *, const subpicture_t *,
47                         vlc_bool_t );
48
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 )
59 {
60     switch( p_vout->output.i_chroma )
61     {
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 );
67             break;
68
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 );
72             break;
73
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 );
78             break;
79
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 );
83             break;
84
85         default:
86             msg_Err( p_vout, "unknown chroma, can't render SPU" );
87             break;
88     }
89 }
90
91 /* Following functions are local */
92
93 static void RenderI420( vout_thread_t *p_vout, picture_t *p_pic,
94                         const subpicture_t *p_spu, vlc_bool_t b_crop )
95 {
96     /* Common variables */
97     uint8_t *p_dest;
98     uint8_t *p_destptr;
99     uint16_t *p_source = (uint16_t *)p_spu->p_sys->p_data;
100
101     int i_x, i_y;
102     int i_len, i_color;
103     uint16_t i_colprecomp, i_destalpha;
104
105     /* Crop-specific */
106     int i_x_start, i_y_start, i_x_end, i_y_end;
107
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 );
110
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 );
115
116     /* Draw until we reach the bottom of the subtitle */
117     for( i_y = p_spu->i_height * p_pic->Y_PITCH ;
118          i_y ;
119          i_y -= p_pic->Y_PITCH )
120     {
121         /* Draw until we reach the end of the line */
122         for( i_x = p_spu->i_width ; i_x ; i_x -= i_len )
123         {
124             /* Get the RLE part, then draw the line */
125             i_color = *p_source & 0x3;
126             i_len = *p_source++ >> 2;
127
128             if( b_crop
129                  && ( i_x < i_x_start || i_x > i_x_end
130                        || i_y < i_y_start || i_y > i_y_end ) )
131             {
132                 continue;
133             }
134
135             switch( p_spu->p_sys->pi_alpha[i_color] )
136             {
137                 case 0x00:
138                     break;
139
140                 case 0x0f:
141                     memset( p_dest - i_x - i_y,
142                             p_spu->p_sys->pi_yuv[i_color][0], i_len );
143                     break;
144
145                 default:
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];
152
153                     for ( p_destptr = p_dest - i_x - i_y;
154                           p_destptr < p_dest - i_x - i_y + i_len;
155                           p_destptr++ )
156                     {
157                         *p_destptr = ( i_colprecomp +
158                                         (uint16_t)*p_destptr * i_destalpha ) >> 4;
159                     }
160                     break;
161             }
162         }
163     }
164 }
165
166 static void RenderRV16( vout_thread_t *p_vout, picture_t *p_pic,
167                         const subpicture_t *p_spu, vlc_bool_t b_crop )
168 {
169     /* Common variables */
170     uint16_t p_clut16[4];
171     uint8_t *p_dest;
172     uint16_t *p_source = (uint16_t *)p_spu->p_sys->p_data;
173
174     int i_x, i_y;
175     int i_len, i_color;
176
177     /* RGB-specific */
178     int i_xscale, i_yscale, i_width, i_height, i_ytmp, i_yreal, i_ynext;
179
180     /* Crop-specific */
181     int i_x_start, i_y_start, i_x_end, i_y_end;
182
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++ )
186     {
187         p_clut16[i_color] = 0x1111
188                              * ( (uint16_t)p_spu->p_sys->pi_yuv[i_color][0] >> 4 );
189     }
190
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;
193
194     i_width  = p_spu->i_width  * i_xscale;
195     i_height = p_spu->i_height * i_yscale;
196
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;
201
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;
206
207     /* Draw until we reach the bottom of the subtitle */
208     for( i_y = 0 ; i_y < i_height ; )
209     {
210         i_ytmp = i_y >> 6;
211         i_y += i_yscale;
212
213         /* Check whether we need to draw one line or more than one */
214         if( i_ytmp + 1 >= ( i_y >> 6 ) )
215         {
216             /* Just one line : we precalculate i_y >> 6 */
217             i_yreal = p_pic->p->i_pitch * i_ytmp;
218
219             /* Draw until we reach the end of the line */
220             for( i_x = i_width ; i_x ; i_x -= i_len )
221             {
222                 /* Get the RLE part, then draw the line */
223                 i_color = *p_source & 0x3;
224                 i_len = i_xscale * ( *p_source++ >> 2 );
225
226                 if( b_crop
227                      && ( i_x < i_x_start || i_x > i_x_end
228                            || i_y < i_y_start || i_y > i_y_end ) )
229                 {
230                     continue;
231                 }
232
233                 switch( p_spu->p_sys->pi_alpha[ i_color ] )
234                 {
235                 case 0x00:
236                     break;
237
238                 case 0x0f:
239                     memset( p_dest - 2 * ( i_x >> 6 ) + i_yreal,
240                             p_clut16[ i_color ],
241                             2 * ( ( i_len >> 6 ) + 1 ) );
242                     break;
243
244                 default:
245                     /* FIXME: we should do transparency */
246                     memset( p_dest - 2 * ( i_x >> 6 ) + i_yreal,
247                             p_clut16[ i_color ],
248                             2 * ( ( i_len >> 6 ) + 1 ) );
249                     break;
250                 }
251             }
252         }
253         else
254         {
255             i_yreal = p_pic->p->i_pitch * i_ytmp;
256             i_ynext = p_pic->p->i_pitch * i_y >> 6;
257
258             /* Draw until we reach the end of the line */
259             for( i_x = i_width ; i_x ; i_x -= i_len )
260             {
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 );
264
265                 if( b_crop
266                      && ( i_x < i_x_start || i_x > i_x_end
267                            || i_y < i_y_start || i_y > i_y_end ) )
268                 {
269                     continue;
270                 }
271
272                 switch( p_spu->p_sys->pi_alpha[ i_color ] )
273                 {
274                 case 0x00:
275                     break;
276
277                 case 0x0f:
278                     for( i_ytmp = i_yreal ; i_ytmp < i_ynext ;
279                          i_ytmp += p_pic->p->i_pitch )
280                     {
281                         memset( p_dest - 2 * ( i_x >> 6 ) + i_ytmp,
282                                 p_clut16[ i_color ],
283                                 2 * ( ( i_len >> 6 ) + 1 ) );
284                     }
285                     break;
286
287                 default:
288                     /* FIXME: we should do transparency */
289                     for( i_ytmp = i_yreal ; i_ytmp < i_ynext ;
290                          i_ytmp += p_pic->p->i_pitch )
291                     {
292                         memset( p_dest - 2 * ( i_x >> 6 ) + i_ytmp,
293                                 p_clut16[ i_color ],
294                                 2 * ( ( i_len >> 6 ) + 1 ) );
295                     }
296                     break;
297                 }
298             }
299         }
300     }
301 }
302
303 static void RenderRV32( vout_thread_t *p_vout, picture_t *p_pic,
304                         const subpicture_t *p_spu, vlc_bool_t b_crop )
305 {
306     /* Common variables */
307     uint32_t p_clut32[4];
308     uint8_t *p_dest;
309     uint16_t *p_source = (uint16_t *)p_spu->p_sys->p_data;
310
311     int i_x, i_y;
312     int i_len, i_color;
313
314     /* RGB-specific */
315     int i_xscale, i_yscale, i_width, i_height, i_ytmp, i_yreal, i_ynext;
316
317     /* Crop-specific */
318     int i_x_start, i_y_start, i_x_end, i_y_end;
319
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++ )
323     {
324         p_clut32[i_color] = 0x11111111
325                              * ( (uint16_t)p_spu->p_sys->pi_yuv[i_color][0] >> 4 );
326     }
327
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;
330
331     i_width  = p_spu->i_width  * i_xscale;
332     i_height = p_spu->i_height * i_yscale;
333
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;
338
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;
343
344     /* Draw until we reach the bottom of the subtitle */
345     for( i_y = 0 ; i_y < i_height ; )
346     {
347         i_ytmp = i_y >> 6;
348         i_y += i_yscale;
349
350         /* Check whether we need to draw one line or more than one */
351         if( i_ytmp + 1 >= ( i_y >> 6 ) )
352         {
353             /* Just one line : we precalculate i_y >> 6 */
354             i_yreal = p_pic->p->i_pitch * i_ytmp;
355
356             /* Draw until we reach the end of the line */
357             for( i_x = i_width ; i_x ; i_x -= i_len )
358             {
359                 /* Get the RLE part, then draw the line */
360                 i_color = *p_source & 0x3;
361                 i_len = i_xscale * ( *p_source++ >> 2 );
362
363                 if( b_crop
364                      && ( i_x < i_x_start || i_x > i_x_end
365                            || i_y < i_y_start || i_y > i_y_end ) )
366                 {
367                     continue;
368                 }
369
370                 switch( p_spu->p_sys->pi_alpha[ i_color ] )
371                 {
372                 case 0x00:
373                     break;
374
375                 case 0x0f:
376                     memset( p_dest - 4 * ( i_x >> 6 ) + i_yreal,
377                             p_clut32[ i_color ], 4 * ( ( i_len >> 6 ) + 1 ) );
378                     break;
379
380                 default:
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 ) );
384                     break;
385                 }
386             }
387         }
388         else
389         {
390             i_yreal = p_pic->p->i_pitch * i_ytmp;
391             i_ynext = p_pic->p->i_pitch * i_y >> 6;
392
393             /* Draw until we reach the end of the line */
394             for( i_x = i_width ; i_x ; i_x -= i_len )
395             {
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 );
399
400                 if( b_crop
401                      && ( i_x < i_x_start || i_x > i_x_end
402                            || i_y < i_y_start || i_y > i_y_end ) )
403                 {
404                     continue;
405                 }
406
407                 switch( p_spu->p_sys->pi_alpha[ i_color ] )
408                 {
409                 case 0x00:
410                     break;
411
412                 case 0x0f:
413                     for( i_ytmp = i_yreal ; i_ytmp < i_ynext ;
414                          i_ytmp += p_pic->p->i_pitch )
415                     {
416                         memset( p_dest - 4 * ( i_x >> 6 ) + i_ytmp,
417                                 p_clut32[ i_color ],
418                                 4 * ( ( i_len >> 6 ) + 1 ) );
419                     }
420                     break;
421
422                 default:
423                     /* FIXME: we should do transparency */
424                     for( i_ytmp = i_yreal ; i_ytmp < i_ynext ;
425                          i_ytmp += p_pic->p->i_pitch )
426                     {
427                         memset( p_dest - 4 * ( i_x >> 6 ) + i_ytmp,
428                                 p_clut32[ i_color ],
429                                 4 * ( ( i_len >> 6 ) + 1 ) );
430                     }
431                     break;
432                 }
433             }
434         }
435     }
436 }
437
438 static void RenderYUY2( vout_thread_t *p_vout, picture_t *p_pic,
439                         const subpicture_t *p_spu, vlc_bool_t b_crop )
440 {
441     /* Common variables */
442     uint8_t *p_dest;
443     uint16_t *p_source = (uint16_t *)p_spu->p_sys->p_data;
444
445     int i_x, i_y;
446     int i_len, i_color;
447     uint8_t i_cnt;
448
449     /* Crop-specific */
450     int i_x_start, i_y_start, i_x_end, i_y_end;
451
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
455
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;
462
463     /* Draw until we reach the bottom of the subtitle */
464     for( i_y = p_spu->i_height * p_pic->p->i_pitch / 2;
465          i_y ;
466          i_y -= p_pic->p->i_pitch / 2 )
467     {
468         /* Draw until we reach the end of the line */
469         for( i_x = p_spu->i_width ; i_x ; i_x -= i_len )
470         {
471             /* Get the RLE part, then draw the line */
472             i_color = *p_source & 0x3;
473             i_len = *p_source++ >> 2;
474
475             if( b_crop
476                  && ( i_x < i_x_start || i_x > i_x_end
477                        || i_y < i_y_start || i_y > i_y_end ) )
478             {
479                 continue;
480             }
481
482             switch( p_spu->p_sys->pi_alpha[ i_color ] )
483             {
484             case 0x00:
485                 break;
486
487             case 0x0f:
488                 for( i_cnt = 0; i_cnt < i_len; i_cnt++ )
489                 {
490                     /* draw a pixel */
491                     /* Y */
492                     memset( p_dest - i_x * 2 - i_y * 2 + i_cnt * 2,
493                             p_spu->p_sys->pi_yuv[i_color][0], 1);
494
495                     if (!(i_cnt & 0x01))
496                     {
497                         /* U and V */
498                         memset( p_dest - i_x * 2 - i_y * 2 + i_cnt * 2 + 1,
499                                 0x80, 1);
500                         memset( p_dest - i_x * 2 - i_y * 2 + i_cnt * 2 + 3,
501                                 0x80, 1);
502                     }
503                 }
504                 break;
505
506             default:
507                 /* FIXME: we should do transparency */
508                 for( i_cnt = 0; i_cnt < i_len; i_cnt++ )
509                 {
510                     /* draw a pixel */
511                     /* Y */
512                     memset( p_dest - i_x * 2 - i_y * 2 + i_cnt * 2,
513                             p_spu->p_sys->pi_yuv[i_color][0], 1);
514
515                     if (!(i_cnt & 0x01))
516                     {
517                         /* U and V */
518                         memset( p_dest - i_x * 2 - i_y * 2 + i_cnt * 2 + 1,
519                                 0x80, 1);
520                         memset( p_dest - i_x * 2 - i_y * 2 + i_cnt * 2 + 3,
521                                 0x80, 1);
522                     }
523                 }
524                 break;
525             }
526         }
527     }
528 }
529