]> git.sesse.net Git - vlc/blob - src/video_output/video_text.c
8dc5ed13fe378810b0a667c32814f41ce8449c05
[vlc] / src / video_output / video_text.c
1 /*****************************************************************************
2  * video_text.c : text manipulation functions
3  *****************************************************************************
4  * Copyright (C) 1999, 2000 VideoLAN
5  * $Id: video_text.c,v 1.20 2001/03/21 13:42:35 sam Exp $
6  *
7  * Authors: Vincent Seguin <seguin@via.ecp.fr>
8  *          Samuel Hocevar <sam@zoy.org>
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 <errno.h>                                                  /* errno */
31 #include <stdlib.h>                                                /* free() */
32 #include <stdio.h>                                              /* sprintf() */
33 #include <string.h>                                            /* strerror() */
34 #include <fcntl.h>                                                 /* open() */
35 #include <unistd.h>                                       /* read(), close() */
36
37 #ifdef SYS_BEOS
38 #include "beos_specific.h"
39 #endif
40
41 #include "config.h"
42 #include "common.h"
43 #include "video_text.h"
44
45 #include "intf_msg.h"
46
47 /*****************************************************************************
48  * vout_font_t: bitmap font
49  *****************************************************************************
50  * This structure is used when the system doesn't provide a convenient function
51  * to print simple characters in a buffer.
52  * VOUT_FIXED_FONTs are stored in raw mode, character after character, with a
53  * first array of characters followed by a second array of borders masks.
54  * Therefore the border masks can't be complete if the font has pixels on the
55  * border.
56  *****************************************************************************/
57 typedef struct vout_font_s
58 {
59     int                 i_type;                                 /* font type */
60     int                 i_width;                /* character width in pixels */
61     int                 i_height;              /* character height in pixels */
62     int                 i_interspacing; /* characters interspacing in pixels */
63     int                 i_bytes_per_line;        /* bytes per character line */
64     int                 i_bytes_per_char;             /* bytes per character */
65     u16                 i_first;                          /* first character */
66     u16                 i_last;                            /* last character */
67     byte_t *            p_data;                       /* font character data */
68 } vout_font_t;
69
70 /* Font types */
71 #define VOUT_FIXED_FONT       0                         /* simple fixed font */
72
73 /*****************************************************************************
74  * vout_put_byte_t: PutByte function
75  *****************************************************************************
76  * These functions will transform masks in a set of pixels. For each pixel,
77  * character, then border and background masks are tested, and the first
78  * encountered color is set.
79  *****************************************************************************/
80 typedef void (vout_put_byte_t)( void *p_pic, int i_byte, int i_char, int i_border,
81                                 int i_bg, u32 i_char_color, u32 i_border_color, u32 i_bg_color );
82
83
84 /*****************************************************************************
85  * Macros
86  *****************************************************************************/
87
88 /* PUT_BYTE_MASK: put pixels from a byte-wide mask. It uses a branching tree
89  * to optimize the number of tests. It is used in the PutByte functions.
90  * This macro works for 1, 2 and 4 Bpp. */
91 #define PUT_BYTE_MASK( i_mask, i_mask_color )                                 \
92 if( i_mask & 0xf0 )                                       /* one from 1111 */ \
93 {                                                                             \
94     if( i_mask & 0xc0 )                                   /* one from 1100 */ \
95     {                                                                         \
96         if( i_mask & 0x80 )                                        /* 1000 */ \
97         {                                                                     \
98             p_pic[0] = i_mask_color;                                          \
99             if( i_mask & 0x40 )                                    /* 0100 */ \
100             {                                                                 \
101                 p_pic[1] = i_mask_color;                                      \
102             }                                                                 \
103         }                                                                     \
104         else                                        /* not 1000 means 0100 */ \
105         {                                                                     \
106             p_pic[1] = i_mask_color;                                          \
107         }                                                                     \
108         if( i_mask & 0x30 )                               /* one from 0011 */ \
109         {                                                                     \
110             if( i_mask & 0x20 )                                    /* 0010 */ \
111             {                                                                 \
112                 p_pic[2] = i_mask_color;                                      \
113                 if( i_mask & 0x10 )                                /* 0001 */ \
114                 {                                                             \
115                     p_pic[3] = i_mask_color;                                  \
116                 }                                                             \
117             }                                                                 \
118             else                                    /* not 0010 means 0001 */ \
119             {                                                                 \
120                  p_pic[3] = i_mask_color;                                     \
121             }                                                                 \
122         }                                                                     \
123     }                                                                         \
124     else                                            /* not 1100 means 0011 */ \
125     {                                                                         \
126         if( i_mask & 0x20 )                                        /* 0010 */ \
127         {                                                                     \
128             p_pic[2] = i_mask_color;                                          \
129             if( i_mask & 0x10 )                                    /* 0001 */ \
130             {                                                                 \
131                 p_pic[3] = i_mask_color;                                      \
132             }                                                                 \
133         }                                                                     \
134         else                                        /* not 0010 means 0001 */ \
135         {                                                                     \
136             p_pic[3] = i_mask_color;                                          \
137         }                                                                     \
138     }                                                                         \
139 }                                                                             \
140 if( i_mask & 0x0f )                                                           \
141 {                                                                             \
142     if( i_mask & 0x0c )                       /* one from 1100 */             \
143     {                                                                         \
144         if( i_mask & 0x08 )                                        /* 1000 */ \
145         {                                                                     \
146             p_pic[4] = i_mask_color;                                          \
147             if( i_mask & 0x04 )                                    /* 0100 */ \
148             {                                                                 \
149                 p_pic[5] = i_mask_color;                                      \
150             }                                                                 \
151         }                                                                     \
152         else                                        /* not 1000 means 0100 */ \
153         {                                                                     \
154             p_pic[5] = i_mask_color;                                          \
155         }                                                                     \
156         if( i_mask & 0x03 )                               /* one from 0011 */ \
157         {                                                                     \
158             if( i_mask & 0x02 )                                    /* 0010 */ \
159             {                                                                 \
160                 p_pic[6] = i_mask_color;                                      \
161                 if( i_mask & 0x01 )                                /* 0001 */ \
162                 {                                                             \
163                     p_pic[7] = i_mask_color;                                  \
164                 }                                                             \
165             }                                                                 \
166             else                                    /* not 0010 means 0001 */ \
167             {                                                                 \
168                  p_pic[7] = i_mask_color;                                     \
169             }                                                                 \
170         }                                                                     \
171     }                                                                         \
172     else                                            /* not 1100 means 0011 */ \
173     {                                                                         \
174         if( i_mask & 0x02 )                                        /* 0010 */ \
175         {                                                                     \
176             p_pic[6] = i_mask_color;                                          \
177             if( i_mask & 0x01 )                                    /* 0001 */ \
178             {                                                                 \
179                 p_pic[7] = i_mask_color;                                      \
180             }                                                                 \
181         }                                                                     \
182         else                                        /* not 0010 means 0001 */ \
183         {                                                                     \
184             p_pic[7] = i_mask_color;                                          \
185         }                                                                     \
186     }                                                                         \
187 }
188
189 /*****************************************************************************
190  * Local prototypes
191  *****************************************************************************/
192 static void PutByte8 ( u8 *p_pic, int i_byte, int i_char, int i_border,
193                        int i_bg, u32 i_char_color, u32 i_border_color,
194                        u32 i_bg_color );
195 static void PutByte16( u16 *p_pic, int i_byte, int i_char, int i_border,
196                        int i_bg, u32 i_char_color, u32 i_border_color,
197                        u32 i_bg_color );
198 static void PutByte24( void *p_pic, int i_byte, byte_t i_char, byte_t i_border,
199                        byte_t i_bg, u32 i_char_color, u32 i_border_color,
200                        u32 i_bg_color );
201 static void PutByte32( u32 *p_pic, int i_byte, byte_t i_char, byte_t i_border,
202                        byte_t i_bg, u32 i_char_color, u32 i_border_color,
203                        u32 i_bg_color );
204
205 /*****************************************************************************
206  * vout_LoadFont: load a bitmap font from a file
207  *****************************************************************************
208  * This function will try to open a .psf font and load it. It will return
209  * NULL on error.
210  *****************************************************************************/
211 vout_font_t *vout_LoadFont( const char *psz_name )
212 {
213     static char * path[] = { "share", DATA_PATH, NULL };
214
215     char **             ppsz_path = path;
216     char *              psz_file;
217 #ifdef SYS_BEOS
218     char *              psz_vlcpath = beos_GetProgramPath();
219     int                 i_vlclen = strlen( psz_vlcpath );
220 #endif
221     int                 i_char, i_line;        /* character and line indexes */
222     int                 i_file = -1;                          /* source file */
223     byte_t              pi_buffer[2];                         /* file buffer */
224     vout_font_t *       p_font;                           /* the font itself */
225
226     for( ; *ppsz_path != NULL ; ppsz_path++ )
227     {
228 #ifdef SYS_BEOS
229         /* Under BeOS, we need to add beos_GetProgramPath() to access
230          * files under the current directory */
231         if( strncmp( *ppsz_path, "/", 1 ) )
232         {
233             psz_file = malloc( strlen( psz_name ) + strlen( *ppsz_path )
234                                 + i_vlclen + 3 );
235             if( psz_file == NULL )
236             {
237                 continue;
238             }
239             sprintf( psz_file, "%s/%s/%s", psz_vlcpath, *ppsz_path, psz_name );
240         }
241         else
242 #endif
243         {
244             psz_file = malloc( strlen( psz_name ) + strlen( *ppsz_path ) + 2 );
245             if( psz_file == NULL )
246             {
247                 continue;
248             }
249             sprintf( psz_file, "%s/%s", *ppsz_path, psz_name );
250         }
251
252         /* Open file */
253         i_file = open( psz_file, O_RDONLY );
254         free( psz_file );
255
256         if( i_file != -1 )
257         {
258             break;
259         }
260     }
261
262     if( i_file == -1 )
263     {
264         intf_DbgMsg( "vout error: can't open file '%s' (%s)",
265                      psz_name, strerror(errno) );
266         return( NULL );
267     }
268
269     /* Read magick number */
270     if( read( i_file, pi_buffer, 2 ) != 2 )
271     {
272         intf_ErrMsg("vout error: unexpected end of file '%s'", psz_name );
273         close( i_file );
274         return( NULL );
275     }
276
277     /* Allocate font descriptor */
278     p_font = malloc( sizeof( vout_font_t ) );
279     if( p_font == NULL )
280     {
281         intf_ErrMsg( "vout error: cannot allocate vout_font_t (%s)",
282                      strerror(ENOMEM) );
283         close( i_file );
284         return( NULL );
285     }
286
287     /* Read file */
288     switch( ((u16)pi_buffer[0] << 8) | pi_buffer[1] )
289     {
290     case 0x3604:                                              /* .psf file */
291         /*
292          * PSF font: simple fixed font. Only the first 256 characters are read.
293          * Those fonts are always 1 byte wide, and 256 or 512 characters long.
294          */
295
296         /* Read font header - two bytes indicate the font properties */
297         if( read( i_file, pi_buffer, 2 ) != 2)
298         {
299             intf_ErrMsg( "vout error: unexpected end of file '%s'", psz_name );
300             free( p_font );
301             close( i_file );
302             return( NULL );
303         }
304
305         /* Copy font properties */
306         p_font->i_type =                VOUT_FIXED_FONT;
307         p_font->i_width =               8;
308         p_font->i_height =              pi_buffer[1];
309         p_font->i_interspacing =        8;
310         p_font->i_bytes_per_line =      1;
311         p_font->i_bytes_per_char =      pi_buffer[1];
312         p_font->i_first =               0;
313         p_font->i_last =                255;
314
315         /* Allocate font space */
316         p_font->p_data = malloc( 2 * 256 * pi_buffer[1] );
317         if( p_font->p_data == NULL )
318         {
319             intf_ErrMsg( "vout error: cannot allocate font space (%s)",
320                          strerror(ENOMEM) );
321             free( p_font );
322             close( i_file );
323             return( NULL );
324         }
325
326         /* Copy raw data */
327         if( read( i_file, p_font->p_data, 256 * pi_buffer[1] ) != 256 * pi_buffer[1] )
328         {
329             intf_ErrMsg("vout error: unexpected end of file '%s'", psz_name );
330             free( p_font->p_data );
331             free( p_font );
332             close( i_file );
333             return( NULL );
334         }
335
336         /* Computes border masks - remember that masks have the same matrix as
337          * characters, so an empty character border is required to have a complete
338          * border mask. */
339         for( i_char = 0; i_char <= 255; i_char++ )
340         {
341             for( i_line = 0; i_line < pi_buffer[1]; i_line++ )
342             {
343
344                 p_font->p_data[ (i_char + 256) * pi_buffer[1] + i_line ] =
345                     ((p_font->p_data[ i_char * pi_buffer[1] + i_line ] << 1) |
346                      (p_font->p_data[ i_char * pi_buffer[1] + i_line ] >> 1) |
347                      (i_line > 0 ? p_font->p_data[ i_char * pi_buffer[1] + i_line - 1]: 0) |
348                      (i_line < pi_buffer[1] - 1 ? p_font->p_data[ i_char * pi_buffer[1] + i_line + 1]: 0))
349                     & ~p_font->p_data[ i_char * pi_buffer[1] + i_line ];
350             }
351         }
352
353         break;
354     default:
355         intf_ErrMsg("vout error: file '%s' has an unknown format", psz_name );
356         free( p_font );
357         close( i_file );
358         return( NULL );
359         break;
360     }
361
362
363     intf_DbgMsg( "loaded %s: type %d, %d-%dx%d", psz_name, p_font->i_type,
364                  p_font->i_width, p_font->i_interspacing, p_font->i_height );
365     return( p_font );
366 }
367
368 /*****************************************************************************
369  * vout_UnloadFont: unload a font
370  *****************************************************************************
371  * This function free the resources allocated by vout_LoadFont
372  *****************************************************************************/
373 void vout_UnloadFont( vout_font_t *p_font )
374 {
375     intf_DbgMsg( "vout: unloading font %p", p_font );
376     free( p_font->p_data );
377     free( p_font );
378 }
379
380 /*****************************************************************************
381  * vout_TextSize: return the dimensions of a text
382  *****************************************************************************
383  * This function is used to align text. It returns the width and height of a
384  * given text.
385  *****************************************************************************/
386 void vout_TextSize( vout_font_t *p_font, int i_style, const char *psz_text, int *pi_width, int *pi_height )
387 {
388     switch( p_font->i_type )
389     {
390     case VOUT_FIXED_FONT:
391         *pi_width  = ((i_style & WIDE_TEXT) ? p_font->i_interspacing * 2 : p_font->i_interspacing) *
392             (strlen( psz_text ) - 1) + p_font->i_width;
393         *pi_height = p_font->i_height;
394         if( i_style & ITALIC_TEXT )
395         {
396             *pi_width = *pi_height / 3;
397         }
398         break;
399 #ifdef DEBUG
400     default:
401         intf_DbgMsg("error: unknown font type %d", p_font->i_type );
402         break;
403 #endif
404     }
405 }
406
407 /*****************************************************************************
408  * vout_Print: low level printing function
409  *****************************************************************************
410  * This function prints a text, without clipping, in a buffer using a
411  * previously loaded bitmap font.
412  *****************************************************************************/
413 void vout_Print( vout_font_t *p_font, byte_t *p_pic, int i_bytes_per_pixel, int i_bytes_per_line,
414                  u32 i_char_color, u32 i_border_color, u32 i_bg_color, int i_style, const char *psz_text, int i_percent)
415 {
416     byte_t      *p_char, *p_border;        /* character and border mask data */
417     int         i_char_mask, i_border_mask, i_bg_mask;              /* masks */
418     int         i_line;                         /* current line in character */
419     int         i_byte;                         /* current byte in character */
420     int         i_interspacing;                  /* offset between two chars */
421     int         i_font_bytes_per_line, i_font_height;     /* font properties */
422     int         i_position, i_end;                      /* current position  */
423     vout_put_byte_t *p_PutByte;                          /* PutByte function */
424
425     /* FIXME: background: can be something else that whole byte ?? */
426
427     /* Select output function */
428     switch( i_bytes_per_pixel )
429     {
430     case 1:
431         p_PutByte = (vout_put_byte_t *) PutByte8;
432         break;
433     case 2:
434         p_PutByte = (vout_put_byte_t *) PutByte16;
435         break;
436     case 3:
437         p_PutByte = (vout_put_byte_t *) PutByte24;
438         break;
439     case 4:
440     default:
441         p_PutByte = (vout_put_byte_t *) PutByte32;
442         break;
443     }
444
445     /* Choose masks and copy font data to local variables */
446     i_char_mask =               (i_style & VOID_TEXT) ?         0 : 0xff;
447     i_border_mask =             (i_style & OUTLINED_TEXT) ?     0xff : 0;
448     i_bg_mask =                 (i_style & OPAQUE_TEXT) ?       0xff : 0;
449
450     i_font_bytes_per_line =     p_font->i_bytes_per_line;
451     i_font_height =             p_font->i_height;
452     i_interspacing =            i_bytes_per_pixel * ((i_style & WIDE_TEXT) ?
453                                                      p_font->i_interspacing * 2 :
454                                                      p_font->i_interspacing);
455
456     /* compute where to stop... */
457     i_end = (int) (i_percent * strlen(psz_text) / 100LL);
458     if(i_end > strlen(psz_text))
459         i_end = strlen(psz_text);
460     
461     
462     /* Print text */
463     for( i_position = 0; i_position < i_end; i_position++ ,psz_text++ )
464     {
465         /* Check that the character is valid */
466         if( (*psz_text >= p_font->i_first) && (*psz_text <= p_font->i_last) )
467         {
468             /* Select character - bytes per char is always valid, event for
469              * non fixed fonts */
470             p_char =    p_font->p_data + (*psz_text - p_font->i_first) * p_font->i_bytes_per_char;
471             p_border =  p_char + (p_font->i_last - p_font->i_first + 1) * p_font->i_bytes_per_char;
472
473             /* Select base address for output */
474             switch( p_font->i_type )
475             {
476             case VOUT_FIXED_FONT:
477                 /*
478                  * Simple fixed width font
479                  */
480
481                 /* Italic text: shift picture start right */
482                 if( i_style & ITALIC_TEXT )
483                 {
484                     p_pic += i_bytes_per_pixel * (p_font->i_height / 3);
485                 }
486
487                 /* Print character */
488                 for( i_line = 0; i_line < i_font_height; i_line ++ )
489                 {
490                     for( i_byte = 0; i_byte < i_font_bytes_per_line; i_byte++, p_char++, p_border++)
491                     {
492                         /* Put pixels */
493                         p_PutByte( p_pic + i_bytes_per_line * i_line, i_byte,
494                                    *p_char & i_char_mask, *p_border & i_border_mask, i_bg_mask,
495                                    i_char_color, i_border_color, i_bg_color );
496                     }
497
498                     /* Italic text: shift picture start left */
499                     if( (i_style & ITALIC_TEXT) && !(i_line % 3) )
500                     {
501                         p_pic -= i_bytes_per_pixel;
502                     }
503                 }
504
505                 /* Jump to next character */
506                 p_pic += i_interspacing;
507                 break;
508 #ifdef DEBUG
509             default:
510                 intf_DbgMsg("error: unknown font type %d", p_font->i_type );
511                 break;
512 #endif
513             }
514         }
515     
516     }
517 }
518
519 /* following functions are local */
520
521 /*****************************************************************************
522  * PutByte8: print a fixed width font character byte in 1 Bpp
523  *****************************************************************************/
524 static void PutByte8( u8 *p_pic, int i_byte, int i_char, int i_border,
525                        int i_bg, u32 i_char_color, u32 i_border_color,
526                        u32 i_bg_color )
527 {
528     /* Computes position offset and background mask */
529     p_pic += 8 * i_byte;
530     i_bg &= ~(i_char | i_border);
531
532     /* Put character bits */
533     PUT_BYTE_MASK(i_char, i_char_color);
534     PUT_BYTE_MASK(i_border, i_border_color);
535     PUT_BYTE_MASK(i_bg, i_bg_color);
536 }
537
538 /*****************************************************************************
539  * PutByte16: print a fixed width font character byte in 2 Bpp
540  *****************************************************************************/
541 static void PutByte16( u16 *p_pic, int i_byte, int i_char, int i_border,
542                        int i_bg, u32 i_char_color, u32 i_border_color,
543                        u32 i_bg_color )
544 {
545     /* Computes position offset and background mask */
546     p_pic += 8 * i_byte;
547     i_bg &= ~(i_char | i_border);
548
549     /* Put character bits */
550     PUT_BYTE_MASK(i_char, i_char_color);
551     PUT_BYTE_MASK(i_border, i_border_color);
552     PUT_BYTE_MASK(i_bg, i_bg_color);
553 }
554
555 /*****************************************************************************
556  * PutByte24: print a fixed width font character byte in 3 Bpp
557  *****************************************************************************/
558 static void PutByte24( void *p_pic, int i_byte, byte_t i_char, byte_t i_border, byte_t i_bg,
559                        u32 i_char_color, u32 i_border_color, u32 i_bg_color )
560 {
561     /* XXX?? */
562 }
563
564 /*****************************************************************************
565  * PutByte32: print a fixed width font character byte in 4 Bpp
566  *****************************************************************************/
567 static void PutByte32( u32 *p_pic, int i_byte, byte_t i_char, byte_t i_border, byte_t i_bg,
568                        u32 i_char_color, u32 i_border_color, u32 i_bg_color )
569 {
570     /* Computes position offset and background mask */
571     p_pic += 8 * i_byte;
572     i_bg &= ~(i_char | i_border);
573
574     /* Put character bits */
575     PUT_BYTE_MASK(i_char, i_char_color);
576     PUT_BYTE_MASK(i_border, i_border_color);
577     PUT_BYTE_MASK(i_bg, i_bg_color);
578 }
579