1 /*****************************************************************************
2 * subtitler.c : subtitler font routines
3 *****************************************************************************
4 * Copyright (C) 1999, 2000 VideoLAN
6 * Authors: Andrew Flintham <amf@cus.org.uk>
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.
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.
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 *****************************************************************************/
23 /*****************************************************************************
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() */
34 #include <vlc/decoder.h>
37 # include <unistd.h> /* read(), close() */
40 #include <sys/types.h>
45 /*****************************************************************************
46 * subtitler_line : internal structure for an individual line in a subtitle
47 *****************************************************************************/
48 typedef struct subtitler_line_s
50 struct subtitler_line_s * p_next;
54 /*****************************************************************************
56 *****************************************************************************/
57 static uint16_t *PlotSubtitleLine( char *, subtitler_font_t *, int,
59 static void DestroySPU ( subpicture_t * );
61 /*****************************************************************************
62 * subtitler_LoadFont: load a run-length encoded font file into memory
63 *****************************************************************************
64 * RLE font files have the following format:
66 * 2 bytes : magic number: 0x36 0x05
67 * 1 byte : font height in rows
69 * then, per character:
71 * 1 byte : character width in pixels
74 * 1 byte : length of row, in entries
78 * 1 byte : number of pixels of that colour
82 *****************************************************************************/
83 subtitler_font_t* E_(subtitler_LoadFont)( vout_thread_t * p_vout,
84 const char * psz_name )
86 subtitler_font_t * p_font;
95 byte_t pi_buffer[512]; /* file buffer */
97 msg_Dbg( p_vout, "loading font '%s'", psz_name );
99 i_file = open( psz_name, O_RDONLY );
103 msg_Err( p_vout, "can't open font file '%s' (%s)", psz_name,
108 /* Read magick number */
109 if( read( i_file, pi_buffer, 2 ) != 2 )
111 msg_Err( p_vout, "unexpected end of font file '%s'", psz_name );
115 if( pi_buffer[0] != 0x36 || pi_buffer[1] != 0x05 )
117 msg_Err( p_vout, "file '%s' is not a font file", psz_name );
122 p_font = malloc( sizeof( subtitler_font_t ) );
126 msg_Err( p_vout, "out of memory" );
131 /* Read font height */
132 if( read( i_file, pi_buffer, 1 ) != 1 )
134 msg_Err( p_vout, "unexpected end of font file '%s'", psz_name );
139 p_font->i_height = pi_buffer[0];
141 /* Initialise font character data */
142 for( i = 0; i < 256; i++ )
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;
152 /* Read character number */
153 if( read( i_file, pi_buffer, 1 ) != 1)
155 msg_Err( p_vout, "unexpected end of font file '%s'", psz_name );
157 E_(subtitler_UnloadFont)( p_vout, p_font );
160 i_char = pi_buffer[0];
162 /* Character 255 signals the end of the font file */
168 /* Read character width */
169 if( read( i_file, pi_buffer, 1 ) != 1 )
171 msg_Err( p_vout, "unexpected end of font file '%s'", psz_name );
173 E_(subtitler_UnloadFont)( p_vout, p_font );
176 p_font->i_width[ i_char ] = pi_buffer[0];
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);
183 if( p_font->p_length[ i_char] == NULL ||
184 p_font->p_offset[ i_char ] == NULL )
186 msg_Err( p_vout, "out of memory" );
188 E_(subtitler_UnloadFont)( p_vout, p_font );
191 for( i_line=0; i_line < p_font->i_height; i_line ++ )
193 p_font->p_offset[ i_char ][ i_line ] = NULL;
197 for( i_line = 0; i_line < p_font->i_height; i_line ++ )
199 /* Read line length */
200 if( read( i_file, pi_buffer, 1 ) != 1)
202 msg_Err( p_vout, "unexpected end of font file '%s'", psz_name);
203 E_(subtitler_UnloadFont)( p_vout, p_font );
207 i_length = pi_buffer[0];
208 p_font->p_length[ i_char ][ i_line ] = i_length;
210 i_total_length += i_length;
212 /* Read line RLE data */
213 if( read( i_file, pi_buffer, i_length*2 ) != i_length*2)
215 msg_Err( p_vout, "unexpected end of font file '%s'", psz_name);
216 E_(subtitler_UnloadFont)( p_vout, p_font );
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 )
224 msg_Err( p_vout, "out of memory" );
226 E_(subtitler_UnloadFont)( p_vout, p_font );
229 for( i = 0; i < i_length; i++ )
231 *( p_font->p_offset[ i_char ][ i_line ] + i ) =
232 (uint16_t) ( pi_buffer[ i * 2 ] +
233 ( pi_buffer[ i * 2 + 1 ] << 2 ) );
238 /* Set total memory size of character */
239 p_font->i_memory[ i_char ] = i_total_length;
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 )
257 msg_Dbg( p_vout, "unloading font" );
264 for( i_char = 0; i_char < 256; i_char ++ )
266 if( p_font->p_offset[ i_char ] != NULL )
268 for( i_line = 0; i_line < p_font->i_height; i_line++ )
270 if( p_font->p_offset[ i_char ][ i_line ] != NULL )
272 free( p_font->p_offset[ i_char ][ i_line ] );
275 free( p_font->p_offset[ i_char ] );
277 if( p_font->p_length[ i_char ] != NULL )
279 free( p_font->p_length[ i_char ] );
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,
293 subpicture_t * p_spu;
308 subtitler_line_t * p_first_line;
309 subtitler_line_t * p_previous_line;
310 subtitler_line_t * p_line;
314 msg_Err( p_vout, "attempt to use NULL font in subtitle" );
319 p_previous_line = NULL;
321 p_line_start = psz_subtitle;
323 while( *p_line_start != 0 )
326 p_word_start = p_line_start;
327 p_char = p_line_start;
329 while( *p_char != '\n' && *p_char != 0 )
331 i_width += p_font->i_width[ toascii( *p_char ) ];
333 if( i_width > p_vout->output.i_width )
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 )
347 p_word_start = p_char+1;
353 p_line = malloc(sizeof(subtitler_line_t));
357 msg_Err( p_vout, "out of memory" );
361 if( p_first_line == NULL )
363 p_first_line = p_line;
366 if( p_previous_line != NULL )
368 p_previous_line->p_next = p_line;
371 p_previous_line = p_line;
373 p_line->p_next = NULL;
375 p_line->p_text = malloc(( p_char - p_line_start ) +1 );
379 msg_Err( p_vout, "out of memory" );
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;
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 )
394 p_line_start = p_char;
395 if( *p_line_start != 0 )
404 p_line = p_first_line;
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
409 while( p_line != NULL )
413 for( i_x = 0; i_x < strlen( p_line->p_text ); i_x++ )
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 ];
419 if(i_width > i_longest_width)
421 i_longest_width = i_width;
423 p_line = p_line->p_next;
426 /* Allow space for the padding bytes at either edge */
427 i_total_length += p_font->i_height * 2 * i_lines;
429 /* Allocate the subpicture internal data. */
430 p_spu = vout_CreateSubPicture( p_vout, MEMORY_SUBPICTURE );
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 )
444 vout_DestroySubPicture( p_vout, p_spu );
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);
453 p_spu->i_start = i_start;
454 p_spu->i_stop = i_stop;
456 p_spu->b_ephemer = i_stop ? VLC_FALSE : VLC_TRUE;
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;
462 p_spu->p_sys->b_palette = 1;
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;
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;
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;
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;
492 p_spu->p_sys->b_crop = VLC_FALSE;
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;
499 p_data = (uint16_t *)(p_spu->p_sys->p_data);
501 p_line = p_first_line;
502 while( p_line != NULL )
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 );
512 /* SPU is finished - we can ask the video output to display it */
513 vout_DisplaySubPicture( p_vout, p_spu );
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 )
532 for( i_x = 0; i_x< strlen( psz_line ); i_x++ )
534 i_line_width += p_font->i_width[ toascii( *( psz_line+i_x ) ) ]; }
536 for( i_y = 0; i_y < p_font->i_height; i_y++ )
538 /* Pad line to fit box */
539 if( i_line_width < i_total_width )
541 *p_data++ = ((( i_total_width - i_line_width)/2) << 2 );
544 for(i_x = 0; i_x < strlen(psz_line); i_x ++)
546 i_char = toascii( *(psz_line + i_x) );
548 if( p_font->i_width[ i_char ] != 0 )
550 p_rle = p_font->p_offset[ i_char ][ i_y ];
551 i_length = p_font->p_length[ i_char ][ i_y ];
555 memcpy(p_data, p_rle, i_length * sizeof(uint16_t) );
561 /* Pad line to fit box */
562 if( i_line_width < i_total_width )
564 *p_data++ = ((( i_total_width - i_line_width)
565 - (( i_total_width - i_line_width)/2)) << 2 );
572 /*****************************************************************************
573 * DestroySPU: subpicture destructor
574 *****************************************************************************/
575 static void DestroySPU( subpicture_t *p_spu )
577 free( p_spu->p_sys );