]> git.sesse.net Git - vlc/blob - modules/codec/spudec/subtitler.c
* ./configure.ac.in: removed -W in favour of -Wtraditional.
[vlc] / modules / codec / spudec / subtitler.c
1 /*****************************************************************************
2  * subtitler.c : subtitler font routines
3  *****************************************************************************
4  * Copyright (C) 1999, 2000 VideoLAN
5  *
6  * Authors: Andrew Flintham <amf@cus.org.uk>
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 <stdlib.h>                                      /* malloc(), free() */
27 #include <string.h>                                    /* memcpy(), memset() */
28 #include <errno.h>                                                  /* errno */
29 #include <fcntl.h>                                                 /* open() */
30 #include <ctype.h>                                              /* toascii() */
31
32 #include <vlc/vlc.h>
33 #include <vlc/vout.h>
34 #include <vlc/decoder.h>
35
36 #ifdef HAVE_UNISTD_H
37 #   include <unistd.h>                                    /* read(), close() */
38 #endif
39
40 #include <sys/types.h>
41 #include <sys/stat.h>
42
43 #include "spudec.h"
44
45 /*****************************************************************************
46  * subtitler_line : internal structure for an individual line in a subtitle
47  *****************************************************************************/
48 typedef struct subtitler_line_s
49 {
50    struct subtitler_line_s *   p_next;
51    char *                      p_text;
52 } subtitler_line_t;
53
54 /*****************************************************************************
55  * Local prototypes
56  *****************************************************************************/
57 static uint16_t *PlotSubtitleLine( char *, subtitler_font_t *, unsigned int,
58                                    uint16_t * );
59 static void      DestroySPU      ( subpicture_t * );
60
61 /*****************************************************************************
62  * subtitler_LoadFont: load a run-length encoded font file into memory
63  *****************************************************************************
64  * RLE font files have the following format:
65  *
66  *     2 bytes : magic number: 0x36 0x05
67  *     1 byte  : font height in rows
68  *
69  *     then, per character:
70  *     1 byte  : character
71  *     1 byte  : character width in pixels
72  *
73  *         then, per row:
74  *         1 byte  : length of row, in entries
75  *
76  *             then, per entry
77  *             1 byte : colour
78  *             1 byte : number of pixels of that colour
79  *
80  *     to end:
81  *     1 byte : 0xff
82  *****************************************************************************/
83 subtitler_font_t* E_(subtitler_LoadFont)( vout_thread_t * p_vout,
84                                       const char * psz_name )
85 {
86     subtitler_font_t * p_font;
87
88     unsigned int i;
89     unsigned int i_char;
90     unsigned int i_length;
91     unsigned int i_line;
92     unsigned int i_total_length;
93
94     int    i_file;
95     byte_t pi_buffer[512];                                    /* file buffer */
96
97     msg_Dbg( p_vout, "loading font '%s'", psz_name );
98
99     i_file = open( psz_name, O_RDONLY );
100
101     if( i_file == -1 )
102     {
103         msg_Err( p_vout, "can't open font file '%s' (%s)", psz_name,
104                          strerror(errno) );
105         return( NULL );
106     }
107
108     /* Read magick number */
109     if( read( i_file, pi_buffer, 2 ) != 2 )
110     {
111         msg_Err( p_vout, "unexpected end of font file '%s'", psz_name );
112         close( i_file );
113         return( NULL );
114     }
115     if( pi_buffer[0] != 0x36 || pi_buffer[1] != 0x05 )
116     {
117         msg_Err( p_vout, "file '%s' is not a font file", psz_name );
118         close( i_file );
119         return( NULL );
120     }
121
122     p_font = malloc( sizeof( subtitler_font_t ) );
123
124     if( p_font == NULL )
125     {
126         msg_Err( p_vout, "out of memory" );
127         close( i_file );
128         return NULL;
129     }
130
131     /* Read font height */
132     if( read( i_file, pi_buffer, 1 ) != 1 )
133     {
134         msg_Err( p_vout, "unexpected end of font file '%s'", psz_name );
135         free( p_font );
136         close( i_file );
137         return( NULL );
138     }
139     p_font->i_height = pi_buffer[0];
140
141     /* Initialise font character data */
142     for( i = 0; i < 256; i++ )
143     {
144         p_font->i_width[i] = 0;
145         p_font->i_memory[i] = 0;
146         p_font->p_offset[i] = NULL;
147         p_font->p_length[i] = NULL;
148     }
149
150     while(1)
151     {
152         /* Read character number */
153         if( read( i_file, pi_buffer, 1 ) != 1)
154         {
155             msg_Err( p_vout, "unexpected end of font file '%s'", psz_name );
156             close( i_file );
157             E_(subtitler_UnloadFont)( p_vout, p_font );
158             return( NULL );
159         }
160         i_char = pi_buffer[0];
161
162         /* Character 255 signals the end of the font file */
163         if(i_char == 255)
164         {
165             break;
166         }
167
168         /* Read character width */
169         if( read( i_file, pi_buffer, 1 ) != 1 )
170         {
171             msg_Err( p_vout, "unexpected end of font file '%s'", psz_name );
172             close( i_file );
173             E_(subtitler_UnloadFont)( p_vout, p_font );
174             return( NULL );
175         }
176         p_font->i_width[ i_char ] = pi_buffer[0];
177
178         p_font->p_length[ i_char ] = (int *) malloc(
179                                      sizeof(int) * p_font->i_height );
180         p_font->p_offset[ i_char ] = (uint16_t **) malloc(
181                                      sizeof(uint16_t *) * p_font->i_height);
182
183         if( p_font->p_length[ i_char] == NULL ||
184             p_font->p_offset[ i_char ] == NULL )
185         {
186             msg_Err( p_vout, "out of memory" );
187             close( i_file );
188             E_(subtitler_UnloadFont)( p_vout, p_font );
189             return NULL;
190         }
191         for( i_line=0; i_line < p_font->i_height; i_line ++ )
192         {
193             p_font->p_offset[ i_char ][ i_line ] = NULL;
194         }
195
196         i_total_length=0;
197         for( i_line = 0; i_line < p_font->i_height; i_line ++ )
198         {
199             /* Read line length */
200             if( read( i_file, pi_buffer, 1 ) != 1)
201             {
202                 msg_Err( p_vout, "unexpected end of font file '%s'", psz_name);
203                 E_(subtitler_UnloadFont)( p_vout, p_font );
204                 close( i_file );
205                 return( NULL );
206             }
207             i_length = pi_buffer[0];
208             p_font->p_length[ i_char ][ i_line ] = i_length;
209
210             i_total_length += i_length;
211
212             /* Read line RLE data */
213             if( read( i_file, pi_buffer, i_length*2 ) != (int)i_length*2)
214             {
215                 msg_Err( p_vout, "unexpected end of font file '%s'", psz_name);
216                 E_(subtitler_UnloadFont)( p_vout, p_font );
217                 close( i_file );
218                 return( NULL );
219             }
220             p_font->p_offset[ i_char ][ i_line ] =
221                 (uint16_t *) malloc( sizeof( uint16_t ) * i_length );
222             if( p_font->p_offset[ i_char ][ i_line ] == NULL )
223             {
224                 msg_Err( p_vout, "out of memory" );
225                 close( i_file );
226                 E_(subtitler_UnloadFont)( p_vout, p_font );
227                 return NULL;
228             }
229             for( i = 0; i < i_length; i++ )
230             {
231                 *( p_font->p_offset[ i_char ][ i_line ] + i ) =
232                 (uint16_t) ( pi_buffer[ i * 2 ] +
233                              ( pi_buffer[ i * 2 + 1 ] << 2 ) );
234             }
235
236         }
237
238        /* Set total memory size of character */
239        p_font->i_memory[ i_char ] = i_total_length;
240
241     }
242
243     close(i_file);
244
245     return p_font;
246 }
247
248 /*****************************************************************************
249  * subtitler_UnloadFont: unload a run-length encoded font file from memory
250  *****************************************************************************/
251 void E_(subtitler_UnloadFont)( vout_thread_t * p_vout,
252                                subtitler_font_t * p_font )
253 {
254     unsigned int i_char;
255     unsigned int i_line;
256
257     msg_Dbg( p_vout, "unloading font" );
258
259     if( p_font == NULL )
260     {
261         return;
262     }
263
264     for( i_char = 0; i_char < 256; i_char ++ )
265     {
266         if( p_font->p_offset[ i_char ] != NULL )
267         {
268             for( i_line = 0; i_line < p_font->i_height; i_line++ )
269             {
270                 if( p_font->p_offset[ i_char ][ i_line ] != NULL )
271                 {
272                     free( p_font->p_offset[ i_char ][ i_line ] );
273                 }
274             }
275             free( p_font->p_offset[ i_char ] );
276         }
277         if( p_font->p_length[ i_char ] != NULL )
278         {
279             free( p_font->p_length[ i_char ] );
280         }
281     }
282
283     free( p_font );
284 }
285
286 /*****************************************************************************
287  * subtitler_PlotSubtitle: create a subpicture containing the subtitle
288  *****************************************************************************/
289 void subtitler_PlotSubtitle ( vout_thread_t *p_vout , char *psz_subtitle,
290                               subtitler_font_t *p_font, mtime_t i_start,
291                               mtime_t i_stop )
292 {
293     subpicture_t     * p_spu;
294
295     unsigned int       i_x;
296     unsigned int       i_width;
297     unsigned int       i_lines;
298     unsigned int       i_longest_width;
299     unsigned int       i_total_length;
300     unsigned int       i_char;
301
302     uint16_t         * p_data;
303
304     char             * p_line_start;
305     char             * p_word_start;
306     char             * p_char;
307
308     subtitler_line_t * p_first_line;
309     subtitler_line_t * p_previous_line;
310     subtitler_line_t * p_line;
311
312     if( p_font == NULL )
313     {
314         msg_Err( p_vout, "attempt to use NULL font in subtitle" );
315         return;
316     }
317
318     p_first_line = NULL;
319     p_previous_line = NULL;
320
321     p_line_start = psz_subtitle;
322
323     while( *p_line_start != 0 )
324     {
325         i_width = 0;
326         p_word_start = p_line_start;
327         p_char = p_line_start;
328
329         while( *p_char != '\n' && *p_char != 0 )
330         {
331             i_width += p_font->i_width[ toascii( *p_char ) ];
332
333             if( i_width > p_vout->output.i_width )
334             {
335                 /* If the line has more than one word, break at the end of
336                    the previous one. If the line is one very long word,
337                    display as much as we can of it */
338                 if( p_word_start != p_line_start )
339                 {
340                     p_char=p_word_start;
341                 }
342                 break;
343             }
344
345             if( *p_char == ' ' )
346             {
347                 p_word_start = p_char+1;
348             }
349
350             p_char++;
351         }
352
353         p_line = malloc(sizeof(subtitler_line_t));
354
355         if( p_line == NULL )
356         {
357             msg_Err( p_vout, "out of memory" );
358             return;
359         }
360
361         if( p_first_line == NULL )
362         {
363             p_first_line = p_line;
364         }
365
366         if( p_previous_line != NULL )
367         {
368             p_previous_line->p_next = p_line;
369         }
370
371         p_previous_line = p_line;
372
373         p_line->p_next = NULL;
374
375         p_line->p_text = malloc(( p_char - p_line_start ) +1 );
376
377         if( p_line == NULL )
378         {
379             msg_Err( p_vout, "out of memory" );
380             return;
381         }
382
383         /* Copy only the part of the text that is in this line */
384         strncpy( p_line->p_text , p_line_start , p_char - p_line_start );
385         *( p_line->p_text + ( p_char - p_line_start )) = 0;
386
387         /* If we had to break a line because it was too long, ensure that
388            no characters are lost */
389         if( *p_char != '\n' && *p_char != 0 )
390         {
391             p_char--;
392         }
393
394         p_line_start = p_char;
395         if( *p_line_start != 0 )
396         {
397             p_line_start ++;
398         }
399     }
400
401     i_lines = 0;
402     i_longest_width = 0;
403     i_total_length = 0;
404     p_line = p_first_line;
405
406     /* Find the width of the longest line, count the total number of lines,
407        and calculate the amount of memory we need to allocate for the RLE
408        data */
409     while( p_line != NULL )
410     {
411         i_lines++;
412         i_width = 0;
413         for( i_x = 0; i_x < strlen( p_line->p_text ); i_x++ )
414         {
415             i_char = toascii(*(( p_line->p_text )+ i_x ));
416             i_width += p_font->i_width[ i_char ];
417             i_total_length += p_font->i_memory[ i_char ];
418         }
419         if(i_width > i_longest_width)
420         {
421             i_longest_width = i_width;
422         }
423         p_line = p_line->p_next;
424     }
425
426     /* Allow space for the padding bytes at either edge */
427     i_total_length += p_font->i_height * 2 * i_lines;
428
429     /* Allocate the subpicture internal data. */
430     p_spu = vout_CreateSubPicture( p_vout, MEMORY_SUBPICTURE );
431     if( p_spu == NULL )
432     {
433         return;
434     }
435
436     /* Rationale for the "p_spudec->i_rle_size * 4": we are going to
437      * expand the RLE stuff so that we won't need to read nibbles later
438      * on. This will speed things up a lot. Plus, we'll only need to do
439      * this stupid interlacing stuff once. */
440     p_spu->p_sys = malloc( sizeof( subpicture_sys_t )
441                             + i_total_length * sizeof(uint16_t) );
442     if( p_spu->p_sys == NULL )
443     {
444         vout_DestroySubPicture( p_vout, p_spu );
445         return;
446     }
447
448     /* Fill the p_spu structure */
449     p_spu->pf_render = E_(RenderSPU);
450     p_spu->pf_destroy = DestroySPU;
451     p_spu->p_sys->p_data = (uint8_t *)p_spu->p_sys + sizeof(subpicture_sys_t);
452
453     p_spu->i_start = i_start;
454     p_spu->i_stop = i_stop;
455
456     p_spu->b_ephemer = i_stop ? VLC_FALSE : VLC_TRUE;
457
458     /* FIXME: Do we need these two? */
459     p_spu->p_sys->pi_offset[0] = 0;
460     p_spu->p_sys->pi_offset[1] = 0;
461
462     p_spu->p_sys->b_palette = 1;
463
464     /* Colour 0 is transparent */
465     p_spu->p_sys->pi_yuv[0][0] = 0xff;
466     p_spu->p_sys->pi_yuv[0][1] = 0x80;
467     p_spu->p_sys->pi_yuv[0][2] = 0x80;
468     p_spu->p_sys->pi_yuv[0][3] = 0x80;
469     p_spu->p_sys->pi_alpha[0] = 0x0;
470
471     /* Colour 1 is grey */
472     p_spu->p_sys->pi_yuv[1][0] = 0x80;
473     p_spu->p_sys->pi_yuv[1][1] = 0x80;
474     p_spu->p_sys->pi_yuv[1][2] = 0x80;
475     p_spu->p_sys->pi_yuv[1][3] = 0x80;
476     p_spu->p_sys->pi_alpha[1] = 0xf;
477
478     /* Colour 2 is white */
479     p_spu->p_sys->pi_yuv[2][0] = 0xff;
480     p_spu->p_sys->pi_yuv[2][1] = 0xff;
481     p_spu->p_sys->pi_yuv[2][2] = 0xff;
482     p_spu->p_sys->pi_yuv[2][3] = 0xff;
483     p_spu->p_sys->pi_alpha[2] = 0xf;
484
485     /* Colour 3 is black */
486     p_spu->p_sys->pi_yuv[3][0] = 0x00;
487     p_spu->p_sys->pi_yuv[3][1] = 0x00;
488     p_spu->p_sys->pi_yuv[3][2] = 0x00;
489     p_spu->p_sys->pi_yuv[3][3] = 0x00;
490     p_spu->p_sys->pi_alpha[3] = 0xf;
491
492     p_spu->p_sys->b_crop = VLC_FALSE;
493
494     p_spu->i_x = (p_vout->output.i_width - i_longest_width) / 2;
495     p_spu->i_y = p_vout->output.i_height - (p_font->i_height * i_lines);
496     p_spu->i_width = i_longest_width;
497     p_spu->i_height = p_font->i_height*i_lines;
498
499     p_data = (uint16_t *)(p_spu->p_sys->p_data);
500
501     p_line = p_first_line;
502     while( p_line != NULL )
503     {
504         p_data = PlotSubtitleLine( p_line->p_text,
505                                    p_font, i_longest_width, p_data );
506         p_previous_line = p_line;
507         p_line = p_line->p_next;
508         free( p_previous_line->p_text );
509         free( p_previous_line );
510     }
511
512     /* SPU is finished - we can ask the video output to display it */
513     vout_DisplaySubPicture( p_vout, p_spu );
514
515 }
516
517 /*****************************************************************************
518  * PlotSubtitleLine: plot a single line of a subtitle
519  *****************************************************************************/
520 static uint16_t * PlotSubtitleLine ( char *psz_line, subtitler_font_t *p_font,
521                                      unsigned int i_total_width,
522                                      uint16_t *p_data )
523 {
524     unsigned int i_x;
525     unsigned int i_y;
526     unsigned int i_length;
527     unsigned int i_line_width;
528     unsigned int i_char;
529
530     uint16_t * p_rle;
531
532     i_line_width = 0;
533     for( i_x = 0; i_x< strlen( psz_line ); i_x++ )
534     {
535         i_line_width += p_font->i_width[ toascii( *( psz_line+i_x ) ) ]; }
536
537     for( i_y = 0; i_y < p_font->i_height; i_y++ )
538     {
539         /* Pad line to fit box */
540         if( i_line_width < i_total_width )
541         {
542             *p_data++ = ((( i_total_width - i_line_width)/2) << 2 );
543         }
544
545         for(i_x = 0; i_x < strlen(psz_line); i_x ++)
546         {
547             i_char = toascii( *(psz_line + i_x) );
548
549             if( p_font->i_width[ i_char ] != 0 )
550             {
551                 p_rle = p_font->p_offset[ i_char ][ i_y ];
552                 i_length = p_font->p_length[ i_char ][ i_y ];
553
554                 if(p_rle != NULL )
555                 {
556                     memcpy(p_data, p_rle, i_length * sizeof(uint16_t) );
557                     p_data+=i_length;
558                 }
559             }
560         }
561
562         /* Pad line to fit box */
563         if( i_line_width < i_total_width )
564         {
565             *p_data++ = ((( i_total_width - i_line_width)
566                         - (( i_total_width - i_line_width)/2)) << 2 );
567         }
568     }
569
570     return p_data;
571 }
572
573 /*****************************************************************************
574  * DestroySPU: subpicture destructor
575  *****************************************************************************/
576 static void DestroySPU( subpicture_t *p_spu )
577 {
578     free( p_spu->p_sys );
579 }