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* 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 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 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 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 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 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 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 subtitler_UnloadFont( vout_thread_t * p_vout, subtitler_font_t * p_font )
256 msg_Dbg( p_vout, "unloading font" );
263 for( i_char = 0; i_char < 256; i_char ++ )
265 if( p_font->p_offset[ i_char ] != NULL )
267 for( i_line = 0; i_line < p_font->i_height; i_line++ )
269 if( p_font->p_offset[ i_char ][ i_line ] != NULL )
271 free( p_font->p_offset[ i_char ][ i_line ] );
274 free( p_font->p_offset[ i_char ] );
276 if( p_font->p_length[ i_char ] != NULL )
278 free( p_font->p_length[ i_char ] );
285 /*****************************************************************************
286 * subtitler_PlotSubtitle: create a subpicture containing the subtitle
287 *****************************************************************************/
288 void subtitler_PlotSubtitle ( vout_thread_t *p_vout , char *psz_subtitle,
289 subtitler_font_t *p_font, mtime_t i_start,
292 subpicture_t * p_spu;
307 subtitler_line_t * p_first_line;
308 subtitler_line_t * p_previous_line;
309 subtitler_line_t * p_line;
313 msg_Err( p_vout, "attempt to use NULL font in subtitle" );
318 p_previous_line = NULL;
320 p_line_start = psz_subtitle;
322 while( *p_line_start != 0 )
325 p_word_start = p_line_start;
326 p_char = p_line_start;
328 while( *p_char != '\n' && *p_char != 0 )
330 i_width += p_font->i_width[ toascii( *p_char ) ];
332 if( i_width > p_vout->output.i_width )
334 /* If the line has more than one word, break at the end of
335 the previous one. If the line is one very long word,
336 display as much as we can of it */
337 if( p_word_start != p_line_start )
346 p_word_start = p_char+1;
352 p_line = malloc(sizeof(subtitler_line_t));
356 msg_Err( p_vout, "out of memory" );
360 if( p_first_line == NULL )
362 p_first_line = p_line;
365 if( p_previous_line != NULL )
367 p_previous_line->p_next = p_line;
370 p_previous_line = p_line;
372 p_line->p_next = NULL;
374 p_line->p_text = malloc(( p_char - p_line_start ) +1 );
378 msg_Err( p_vout, "out of memory" );
382 /* Copy only the part of the text that is in this line */
383 strncpy( p_line->p_text , p_line_start , p_char - p_line_start );
384 *( p_line->p_text + ( p_char - p_line_start )) = 0;
386 /* If we had to break a line because it was too long, ensure that
387 no characters are lost */
388 if( *p_char != '\n' && *p_char != 0 )
393 p_line_start = p_char;
394 if( *p_line_start != 0 )
403 p_line = p_first_line;
405 /* Find the width of the longest line, count the total number of lines,
406 and calculate the amount of memory we need to allocate for the RLE
408 while( p_line != NULL )
412 for( i_x = 0; i_x < strlen( p_line->p_text ); i_x++ )
414 i_char = toascii(*(( p_line->p_text )+ i_x ));
415 i_width += p_font->i_width[ i_char ];
416 i_total_length += p_font->i_memory[ i_char ];
418 if(i_width > i_longest_width)
420 i_longest_width = i_width;
422 p_line = p_line->p_next;
425 /* Allow space for the padding bytes at either edge */
426 i_total_length += p_font->i_height * 2 * i_lines;
428 /* Allocate the subpicture internal data. */
429 p_spu = vout_CreateSubPicture( p_vout, MEMORY_SUBPICTURE );
435 /* Rationale for the "p_spudec->i_rle_size * 4": we are going to
436 * expand the RLE stuff so that we won't need to read nibbles later
437 * on. This will speed things up a lot. Plus, we'll only need to do
438 * this stupid interlacing stuff once. */
439 p_spu->p_sys = malloc( sizeof( subpicture_sys_t )
440 + i_total_length * sizeof(uint16_t) );
441 if( p_spu->p_sys == NULL )
443 vout_DestroySubPicture( p_vout, p_spu );
447 /* Fill the p_spu structure */
448 p_spu->pf_render = E_(RenderSPU);
449 p_spu->pf_destroy = DestroySPU;
450 p_spu->p_sys->p_data = (uint8_t *)p_spu->p_sys + sizeof(subpicture_sys_t);
452 p_spu->i_start = i_start;
453 p_spu->i_stop = i_stop;
455 p_spu->b_ephemer = i_stop ? VLC_FALSE : VLC_TRUE;
457 /* FIXME: Do we need these two? */
458 p_spu->p_sys->pi_offset[0] = 0;
459 p_spu->p_sys->pi_offset[1] = 0;
461 p_spu->p_sys->b_palette = 1;
463 /* Colour 0 is transparent */
464 p_spu->p_sys->pi_yuv[0][0] = 0xff;
465 p_spu->p_sys->pi_yuv[0][1] = 0x80;
466 p_spu->p_sys->pi_yuv[0][2] = 0x80;
467 p_spu->p_sys->pi_yuv[0][3] = 0x80;
468 p_spu->p_sys->pi_alpha[0] = 0x0;
470 /* Colour 1 is grey */
471 p_spu->p_sys->pi_yuv[1][0] = 0x80;
472 p_spu->p_sys->pi_yuv[1][1] = 0x80;
473 p_spu->p_sys->pi_yuv[1][2] = 0x80;
474 p_spu->p_sys->pi_yuv[1][3] = 0x80;
475 p_spu->p_sys->pi_alpha[1] = 0xf;
477 /* Colour 2 is white */
478 p_spu->p_sys->pi_yuv[2][0] = 0xff;
479 p_spu->p_sys->pi_yuv[2][1] = 0xff;
480 p_spu->p_sys->pi_yuv[2][2] = 0xff;
481 p_spu->p_sys->pi_yuv[2][3] = 0xff;
482 p_spu->p_sys->pi_alpha[2] = 0xf;
484 /* Colour 3 is black */
485 p_spu->p_sys->pi_yuv[3][0] = 0x00;
486 p_spu->p_sys->pi_yuv[3][1] = 0x00;
487 p_spu->p_sys->pi_yuv[3][2] = 0x00;
488 p_spu->p_sys->pi_yuv[3][3] = 0x00;
489 p_spu->p_sys->pi_alpha[3] = 0xf;
491 p_spu->p_sys->b_crop = VLC_FALSE;
493 p_spu->i_x = (p_vout->output.i_width - i_longest_width) / 2;
494 p_spu->i_y = p_vout->output.i_height - (p_font->i_height * i_lines);
495 p_spu->i_width = i_longest_width;
496 p_spu->i_height = p_font->i_height*i_lines;
498 p_data = (uint16_t *)(p_spu->p_sys->p_data);
500 p_line = p_first_line;
501 while( p_line != NULL )
503 p_data = PlotSubtitleLine( p_line->p_text,
504 p_font, i_longest_width, p_data );
505 p_previous_line = p_line;
506 p_line = p_line->p_next;
507 free( p_previous_line->p_text );
508 free( p_previous_line );
511 /* SPU is finished - we can ask the video output to display it */
512 vout_DisplaySubPicture( p_vout, p_spu );
516 /*****************************************************************************
517 * PlotSubtitleLine: plot a single line of a subtitle
518 *****************************************************************************/
519 static uint16_t * PlotSubtitleLine ( char *psz_line, subtitler_font_t *p_font,
520 int i_total_width, uint16_t *p_data )
531 for( i_x = 0; i_x< strlen( psz_line ); i_x++ )
533 i_line_width += p_font->i_width[ toascii( *( psz_line+i_x ) ) ]; }
535 for( i_y = 0; i_y < p_font->i_height; i_y++ )
537 /* Pad line to fit box */
538 if( i_line_width < i_total_width )
540 *p_data++ = ((( i_total_width - i_line_width)/2) << 2 );
543 for(i_x = 0; i_x < strlen(psz_line); i_x ++)
545 i_char = toascii( *(psz_line + i_x) );
547 if( p_font->i_width[ i_char ] != 0 )
549 p_rle = p_font->p_offset[ i_char ][ i_y ];
550 i_length = p_font->p_length[ i_char ][ i_y ];
554 memcpy(p_data, p_rle, i_length * sizeof(uint16_t) );
560 /* Pad line to fit box */
561 if( i_line_width < i_total_width )
563 *p_data++ = ((( i_total_width - i_line_width)
564 - (( i_total_width - i_line_width)/2)) << 2 );
571 /*****************************************************************************
572 * DestroySPU: subpicture destructor
573 *****************************************************************************/
574 static void DestroySPU( subpicture_t *p_spu )
576 free( p_spu->p_sys );