]> git.sesse.net Git - vlc/blob - src/video_output/video_spu.c
bc9aa9c9b71e996f1da16cca3f2cf88869fb9ec3
[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  * Samuel "Sam" Hocevar <sam@via.ecp.fr>
8  * Henri Fallon <henri@via.ecp.fr>
9  * 
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  * 
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
23  *****************************************************************************/
24
25 /*****************************************************************************
26  * Preamble
27  *****************************************************************************/
28 #include "defs.h"
29
30 #include <stdio.h>
31
32 #include "config.h"
33 #include "common.h"
34 #include "threads.h"
35 #include "mtime.h"
36
37 #include "video.h"
38 #include "video_output.h"
39 #include "video_spu.h"
40
41 #include "intf_msg.h"
42
43 typedef struct vout_spu_s
44 {
45     int i_id;
46     byte_t *p_data;
47
48     /* drawing coordinates inside the spu */
49     int i_x;
50     int i_y;
51     /* target size */
52     int i_width;
53     int i_height;
54
55 } vout_spu_t;
56
57 static int NewLine  ( vout_spu_t *p_vspu, int *i_id );
58
59 /* i = get_nibble(); */
60 #define GET_NIBBLE( i ) \
61     if( b_aligned ) \
62     { \
63         i_next = *p_from[i_id]; \
64         p_from[ i_id ]++; \
65         b_aligned = 0; \
66         i = i_next >> 4; \
67     } \
68     else \
69     { \
70         b_aligned = 1; \
71         i = i_next & 0xf; \
72     }
73
74 /* i = j + get_nibble(); */
75 #define ADD_NIBBLE( i, j ) \
76     if( b_aligned ) \
77     { \
78         i_next = *p_from[i_id]; \
79         p_from[ i_id ]++; \
80         b_aligned = 0; \
81         i = (j) + (i_next >> 4); \
82     } \
83     else \
84     { \
85         b_aligned = 1; \
86         i = (j) + (i_next & 0xf); \
87     }
88
89 /*****************************************************************************
90  * vout_RenderSPU: draws an SPU on a picture
91  *****************************************************************************
92  * 
93  *****************************************************************************/
94 void vout_RenderSPU( vout_buffer_t *p_buffer, subpicture_t *p_subpic,
95                      int i_bytes_per_pixel, int i_bytes_per_line )
96 {
97     int i_code = 0x00;
98     int i_next = 0;
99     int i_id = 0;
100     int i_color;
101
102     /* FIXME: we need a way to get this information from the stream */
103     #define TARGET_WIDTH     720
104     #define TARGET_HEIGHT    576
105     int i_x_scale = ( p_buffer->i_pic_width << 6 ) / TARGET_WIDTH;
106     int i_y_scale = ( p_buffer->i_pic_height << 6 ) / TARGET_HEIGHT;
107
108     /* FIXME: fake palette - the real one has to be sought in the .IFO */
109     static int p_palette[4] = { 0x0000, 0xffff, 0x5555, 0x8888 };
110
111     boolean_t b_aligned = 1;
112     byte_t *p_from[2];
113     vout_spu_t vspu;
114
115     p_from[1] = p_subpic->p_data + p_subpic->type.spu.i_offset[1];
116     p_from[0] = p_subpic->p_data + p_subpic->type.spu.i_offset[0];
117
118     vspu.i_x = 0;
119     vspu.i_y = 0;
120     vspu.i_width = TARGET_WIDTH;
121     vspu.i_height = TARGET_HEIGHT;
122     vspu.p_data = p_buffer->p_data
123                     /* add the picture coordinates and the SPU coordinates */
124                     + ( p_buffer->i_pic_x + ((p_subpic->i_x * i_x_scale) >> 6))
125                         * i_bytes_per_pixel
126                     + ( p_buffer->i_pic_y + ((p_subpic->i_y * i_y_scale) >> 6))
127                         * i_bytes_per_line;
128
129     /* Do we need scaling ? 
130      * This is mostly dupliucate code except a few lines.
131      * This test was put out of the loop to avoid testing it
132      * each time.
133      */
134     if ( i_y_scale >= (1 << 6) )
135     {
136         while( p_from[0] < (byte_t *)p_subpic->p_data
137                              + p_subpic->type.spu.i_offset[1] )
138         {
139             GET_NIBBLE( i_code );
140     
141             if( i_code >= 0x04 )
142             {
143                 found_code_with_scale:
144     
145                 if( ((i_code >> 2) + vspu.i_x + vspu.i_y * vspu.i_width)
146                         > vspu.i_height * vspu.i_width )
147                 {
148                     intf_DbgMsg ( "video_spu: invalid draw request ! %d %d",
149                                   i_code >> 2, vspu.i_height * vspu.i_width
150                                    - ( (i_code >> 2) + vspu.i_x
151                                        + vspu.i_y * vspu.i_width ) );
152                     return;
153                 }
154                 else
155                 {
156                     if( (i_color = i_code & 0x3) )
157                     {
158                         u8 *p_target = &vspu.p_data[
159                             i_bytes_per_pixel * ((vspu.i_x * i_x_scale) >> 6)
160                             + i_bytes_per_line * ((vspu.i_y * i_y_scale) >> 6) ];
161     
162                         memset( p_target, p_palette[i_color],
163                                 ((((i_code - 1) * i_x_scale) >> 8) + 1)
164                                 * i_bytes_per_pixel );
165     
166                         /* here we need some horizontal scaling (unlikely )
167                          * we only scale up to 2x, someone watching a DVD
168                          * with more than 2x zoom must be braindead */
169                             p_target += i_bytes_per_line;
170     
171                             memset( p_target, p_palette[i_color],
172                                     ((((i_code - 1) * i_x_scale) >> 8) + 1)
173                                     * i_bytes_per_pixel );
174                     }
175                     vspu.i_x += i_code >> 2;
176                 }
177     
178                 if( vspu.i_x >= vspu.i_width )
179                 {
180                     /* byte-align the stream */
181                     b_aligned = 1;
182                     /* finish the line */
183                     NewLine( &vspu, &i_id );
184                 }
185                 continue;
186             }
187     
188             ADD_NIBBLE( i_code, (i_code << 4) );
189             if( i_code >= 0x10 )   /* 00 11 xx cc */
190                 goto found_code_with_scale;   /* 00 01 xx cc */
191     
192             ADD_NIBBLE( i_code, (i_code << 4) );
193             if( i_code >= 0x040 )  /* 00 00 11 xx xx cc */
194                 goto found_code_with_scale;   /* 00 00 01 xx xx cc */
195     
196             ADD_NIBBLE( i_code, (i_code << 4) );
197             if( i_code >= 0x0100 ) /* 00 00 00 11 xx xx xx cc */
198                 goto found_code_with_scale;   /* 00 00 00 01 xx xx xx cc */
199     
200             /* if the 14 first bits are 0, then it's a newline */
201             if( i_code <= 0x0003 )
202             {
203                 if( NewLine( &vspu, &i_id ) < 0 )
204                     return;
205     
206                 if( !b_aligned )
207                     b_aligned = 1;
208             }
209             else
210             {
211                 /* we have a boo boo ! */
212                 intf_DbgMsg( "video_spu: unknown code 0x%x "
213                              "(dest %x side %x, x=%d, y=%d)",
214                              i_code, p_from[i_id], i_id, vspu.i_x, vspu.i_y );
215                 if( NewLine( &vspu, &i_id ) < 0 )
216                     return;
217                 continue;
218             }
219         }
220     }
221     else
222     {
223         while( p_from[0] < (byte_t *)p_subpic->p_data
224                              + p_subpic->type.spu.i_offset[1] )
225         {
226             GET_NIBBLE( i_code );
227     
228             if( i_code >= 0x04 )
229             {
230                 found_code:
231     
232                 if( ((i_code >> 2) + vspu.i_x + vspu.i_y * vspu.i_width)
233                         > vspu.i_height * vspu.i_width )
234                 {
235                     intf_DbgMsg ( "video_spu: invalid draw request ! %d %d",
236                                   i_code >> 2, vspu.i_height * vspu.i_width
237                                    - ( (i_code >> 2) + vspu.i_x
238                                        + vspu.i_y * vspu.i_width ) );
239                     return;
240                 }
241                 else
242                 {
243                     if( (i_color = i_code & 0x3) )
244                     {
245                         u8 *p_target = &vspu.p_data[
246                             i_bytes_per_pixel * ((vspu.i_x * i_x_scale) >> 6)
247                             + i_bytes_per_line * ((vspu.i_y * i_y_scale) >> 6) ];
248     
249                         memset( p_target, p_palette[i_color],
250                                 ((((i_code - 1) * i_x_scale) >> 8) + 1)
251                                 * i_bytes_per_pixel );
252                     }
253                     vspu.i_x += i_code >> 2;
254                 }
255     
256                 if( vspu.i_x >= vspu.i_width )
257                 {
258                     /* byte-align the stream */
259                     b_aligned = 1;
260                     /* finish the line */
261                     NewLine( &vspu, &i_id );
262                 }
263                 continue;
264             }
265     
266             ADD_NIBBLE( i_code, (i_code << 4) );
267             if( i_code >= 0x10 )   /* 00 11 xx cc */
268                 goto found_code;   /* 00 01 xx cc */
269     
270             ADD_NIBBLE( i_code, (i_code << 4) );
271             if( i_code >= 0x040 )  /* 00 00 11 xx xx cc */
272                 goto found_code;   /* 00 00 01 xx xx cc */
273     
274             ADD_NIBBLE( i_code, (i_code << 4) );
275             if( i_code >= 0x0100 ) /* 00 00 00 11 xx xx xx cc */
276                 goto found_code;   /* 00 00 00 01 xx xx xx cc */
277     
278             /* if the 14 first bits are 0, then it's a newline */
279             if( i_code <= 0x0003 )
280             {
281                 if( NewLine( &vspu, &i_id ) < 0 )
282                     return;
283     
284                 if( !b_aligned )
285                     b_aligned = 1;
286             }
287             else
288             {
289                 /* we have a boo boo ! */
290                 intf_DbgMsg( "video_spu: unknown code 0x%x "
291                              "(dest %x side %x, x=%d, y=%d)",
292                              i_code, p_from[i_id], i_id, vspu.i_x, vspu.i_y );
293                 if( NewLine( &vspu, &i_id ) < 0 )
294                     return;
295                 continue;
296             }
297         }
298     }
299 }
300
301 static int NewLine( vout_spu_t *p_vspu, int *i_id )
302 {
303     *i_id = 1 - *i_id;
304
305     p_vspu->i_x = 0;
306     p_vspu->i_y++;
307
308     return( p_vspu->i_width - p_vspu->i_y );
309
310 }
311