]> git.sesse.net Git - vlc/blob - modules/codec/spudec/subtitler.c
088f44103d426205a0c36e94a20a19b12ff4bb46
[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 *, 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     int                i;
89     int                i_file;
90     int                i_char;
91     int                i_length;
92     int                i_line;
93     int                i_total_length;
94
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 ) != 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     int i_char;
255     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     int                i_x;
296     int                i_width;
297     int                i_lines;
298     int                i_longest_width;
299     int                i_total_length;
300     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                                      int i_total_width, uint16_t *p_data )
522 {
523     int   i_x;
524     int   i_y;
525     int   i_length;
526     int   i_line_width;
527     int   i_char;
528
529     uint16_t * p_rle;
530
531     i_line_width = 0;
532     for( i_x = 0; i_x< strlen( psz_line ); i_x++ )
533     {
534         i_line_width += p_font->i_width[ toascii( *( psz_line+i_x ) ) ]; }
535
536     for( i_y = 0; i_y < p_font->i_height; i_y++ )
537     {
538         /* Pad line to fit box */
539         if( i_line_width < i_total_width )
540         {
541             *p_data++ = ((( i_total_width - i_line_width)/2) << 2 );
542         }
543
544         for(i_x = 0; i_x < strlen(psz_line); i_x ++)
545         {
546             i_char = toascii( *(psz_line + i_x) );
547
548             if( p_font->i_width[ i_char ] != 0 )
549             {
550                 p_rle = p_font->p_offset[ i_char ][ i_y ];
551                 i_length = p_font->p_length[ i_char ][ i_y ];
552
553                 if(p_rle != NULL )
554                 {
555                     memcpy(p_data, p_rle, i_length * sizeof(uint16_t) ); 
556                     p_data+=i_length;
557                 }
558             }
559         }
560     
561         /* Pad line to fit box */
562         if( i_line_width < i_total_width )
563         {
564             *p_data++ = ((( i_total_width - i_line_width)
565                         - (( i_total_width - i_line_width)/2)) << 2 );
566         }
567     }
568
569     return p_data;
570 }
571
572 /*****************************************************************************
573  * DestroySPU: subpicture destructor
574  *****************************************************************************/
575 static void DestroySPU( subpicture_t *p_spu )
576 {
577     free( p_spu->p_sys );
578 }