]> git.sesse.net Git - vlc/blob - src/video_output/video_spu.c
b0e555357c7d12889b6f9b0f16fa48ef5ef14d5f
[vlc] / src / video_output / video_spu.c
1 /*****************************************************************************
2  * video_spu.h : DVD subpicture units functions
3  *****************************************************************************
4  * Copyright (C) 1999, 2000 VideoLAN
5  *
6  * Authors:
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  * 
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
21  *****************************************************************************/
22
23 /*****************************************************************************
24  * Preamble
25  *****************************************************************************/
26 #include "defs.h"
27
28 #include <stdio.h>
29
30 #include "config.h"
31 #include "common.h"
32 #include "threads.h"
33 #include "plugins.h"
34 #include "mtime.h"
35 #include "video.h"
36 #include "video_output.h"
37 #include "video_spu.h"
38
39 #include "intf_msg.h"
40
41 typedef struct vout_spu_s
42 {
43     int i_id;
44     byte_t *p_data;
45
46     /* drawing coordinates inside the spu */
47     int i_x;
48     int i_y;
49     /* target size */
50     int i_width;
51     int i_height;
52
53 } vout_spu_t;
54
55 static int NewLine  ( vout_spu_t *p_vspu, int *i_id );
56
57 /* i = get_nibble(); */
58 #define GET_NIBBLE( i ) \
59     if( b_aligned ) \
60     { \
61         i_next = *p_from[i_id]; \
62         p_from[ i_id ]++; \
63         b_aligned = 0; \
64         i = i_next >> 4; \
65     } \
66     else \
67     { \
68         b_aligned = 1; \
69         i = i_next & 0xf; \
70     }
71
72 /* i = j + get_nibble(); */
73 #define ADD_NIBBLE( i, j ) \
74     if( b_aligned ) \
75     { \
76         i_next = *p_from[i_id]; \
77         p_from[ i_id ]++; \
78         b_aligned = 0; \
79         i = (j) + (i_next >> 4); \
80     } \
81     else \
82     { \
83         b_aligned = 1; \
84         i = (j) + (i_next & 0xf); \
85     }
86
87 /*****************************************************************************
88  * vout_RenderSPU: draws an SPU on a picture
89  *****************************************************************************
90  * 
91  *****************************************************************************/
92 void vout_RenderSPU( vout_buffer_t *p_buffer, subpicture_t *p_subpic,
93                      int i_bytes_per_pixel, int i_bytes_per_line )
94 {
95     int i_code = 0x00;
96     int i_next = 0;
97     int i_id = 0;
98     int i_color;
99
100     /* FIXME: we need a way to get this information from the stream */
101     #define TARGET_WIDTH     720
102     #define TARGET_HEIGHT    576
103     int i_x_scale = ( p_buffer->i_pic_width << 6 ) / TARGET_WIDTH;
104     int i_y_scale = ( p_buffer->i_pic_height << 6 ) / TARGET_HEIGHT;
105
106     /* FIXME: fake palette - the real one has to be sought in the .IFO */
107     static int p_palette[4] = { 0x0000, 0x0000, 0x5555, 0xffff };
108
109     boolean_t b_aligned = 1;
110     byte_t *p_from[2];
111     vout_spu_t vspu;
112
113     p_from[1] = p_subpic->p_data + p_subpic->type.spu.i_offset[1];
114     p_from[0] = p_subpic->p_data + p_subpic->type.spu.i_offset[0];
115
116     vspu.i_x = 0;
117     vspu.i_y = 0;
118     vspu.i_width = TARGET_WIDTH;
119     vspu.i_height = TARGET_HEIGHT;
120     vspu.p_data = p_buffer->p_data
121                     /* add the picture coordinates and the SPU coordinates */
122                     + ( p_buffer->i_pic_x + ((p_subpic->i_x * i_x_scale) >> 6))
123                         * i_bytes_per_pixel
124                     + ( p_buffer->i_pic_y + ((p_subpic->i_y * i_y_scale) >> 6))
125                         * i_bytes_per_line;
126
127     while( p_from[0] < (byte_t *)p_subpic->p_data
128                          + p_subpic->type.spu.i_offset[1] )
129     {
130         GET_NIBBLE( i_code );
131
132         if( i_code >= 0x04 )
133         {
134             found_code:
135
136             if( ((i_code >> 2) + vspu.i_x + vspu.i_y * vspu.i_width)
137                     > vspu.i_height * vspu.i_width )
138             {
139                 intf_DbgMsg ( "video_spu: invalid draw request ! %d %d\n",
140                               i_code >> 2, vspu.i_height * vspu.i_width
141                                - ( (i_code >> 2) + vspu.i_x
142                                    + vspu.i_y * vspu.i_width ) );
143                 return;
144             }
145             else
146             {
147                 if( (i_color = i_code & 0x3) )
148                 {
149                     u8 *p_target = &vspu.p_data[
150                         i_bytes_per_pixel * ((vspu.i_x * i_x_scale) >> 6)
151                         + i_bytes_per_line * ((vspu.i_y * i_y_scale) >> 6) ];
152
153                     memset( p_target, p_palette[i_color],
154                             ((((i_code - 1) * i_x_scale) >> 8) + 1)
155                             * i_bytes_per_pixel );
156
157                     /* if we need some horizontal scaling (unlikely )
158                      * we only scale up to 2x, someone watching a DVD
159                      * with more than 2x zoom must be braindead */
160                     if( i_y_scale >= (1 << 6) )
161                     {
162                         p_target += i_bytes_per_line;
163
164                         memset( p_target, p_palette[i_color],
165                                 ((((i_code - 1) * i_x_scale) >> 8) + 1)
166                                 * i_bytes_per_pixel );
167                     }
168                 }
169                 vspu.i_x += i_code >> 2;
170             }
171
172             if( vspu.i_x >= vspu.i_width )
173             {
174                 /* byte-align the stream */
175                 b_aligned = 1;
176                 /* finish the line */
177                 NewLine( &vspu, &i_id );
178             }
179             continue;
180         }
181
182         ADD_NIBBLE( i_code, (i_code << 4) );
183         if( i_code >= 0x10 )   /* 00 11 xx cc */
184             goto found_code;   /* 00 01 xx cc */
185
186         ADD_NIBBLE( i_code, (i_code << 4) );
187         if( i_code >= 0x040 )  /* 00 00 11 xx xx cc */
188             goto found_code;   /* 00 00 01 xx xx cc */
189
190         ADD_NIBBLE( i_code, (i_code << 4) );
191         if( i_code >= 0x0100 ) /* 00 00 00 11 xx xx xx cc */
192             goto found_code;   /* 00 00 00 01 xx xx xx cc */
193
194         /* if the 14 first bits are 0, then it's a newline */
195         if( i_code <= 0x0003 )
196         {
197             if( NewLine( &vspu, &i_id ) < 0 )
198                 return;
199
200             if( !b_aligned )
201                 b_aligned = 1;
202         }
203         else
204         {
205             /* we have a boo boo ! */
206             intf_DbgMsg( "video_spu: unknown code 0x%x "
207                          "(dest %x side %x, x=%d, y=%d)\n",
208                          i_code, p_from[i_id], i_id, vspu.i_x, vspu.i_y );
209             if( NewLine( &vspu, &i_id ) < 0 )
210                 return;
211             continue;
212         }
213     }
214 }
215
216 static int NewLine( vout_spu_t *p_vspu, int *i_id )
217 {
218     *i_id = 1 - *i_id;
219
220     p_vspu->i_x = 0;
221     p_vspu->i_y++;
222
223     return( p_vspu->i_width - p_vspu->i_y );
224
225 }
226