]> git.sesse.net Git - vlc/blob - modules/misc/win32text.c
5b6fa4c30ba21afba8016d3ce32df8389270b9ee
[vlc] / modules / misc / win32text.c
1 /*****************************************************************************
2  * win32text.c : Text drawing routines using the TextOut win32 API
3  *****************************************************************************
4  * Copyright (C) 2002 - 2005 the VideoLAN team
5  * $Id$
6  *
7  * Authors: Gildas Bazin <gbazin@videolan.org>
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22  *****************************************************************************/
23
24 /*****************************************************************************
25  * Preamble
26  *****************************************************************************/
27 #include <stdlib.h>                                      /* malloc(), free() */
28 #include <string.h>
29
30 #include <vlc/vlc.h>
31 #include <vlc_vout.h>
32 #include <vlc_osd.h>
33 #include <vlc_block.h>
34 #include <vlc_filter.h>
35
36 #include <math.h>
37
38 /*****************************************************************************
39  * Local prototypes
40  *****************************************************************************/
41 static int  Create ( vlc_object_t * );
42 static void Destroy( vlc_object_t * );
43
44 /* The RenderText call maps to pf_render_string, defined in vlc_filter.h */
45 static int RenderText( filter_t *, subpicture_region_t *,
46                        subpicture_region_t * );
47
48 static int Render( filter_t *, subpicture_region_t *, uint8_t *, int, int);
49 static int SetFont( filter_t *, int );
50
51 /*****************************************************************************
52  * Module descriptor
53  *****************************************************************************/
54 #define FONT_TEXT N_("Font")
55 #define FONT_LONGTEXT N_("Filename for the font you want to use")
56 #define FONTSIZE_TEXT N_("Font size in pixels")
57 /// \bug [String] extra space
58 #define FONTSIZE_LONGTEXT N_("This is the default size of the fonts " \
59      "that will be rendered on the video. " \
60      "If set to something different than 0 this option will override the " \
61      "relative font size. " )
62 #define OPACITY_TEXT N_("Opacity")
63 #define OPACITY_LONGTEXT N_("The opacity (inverse of transparency) of the " \
64      "text that will be rendered on the video. 0 = transparent, " \
65      "255 = totally opaque. " )
66 #define COLOR_TEXT N_("Text default color")
67 #define COLOR_LONGTEXT N_("The color of the text that will be rendered on "\
68   "the video. This must be an hexadecimal (like HTML colors). The first two "\
69   "chars are for red, then green, then blue. #000000 = black, #FF0000 = red,"\
70   " #00FF00 = green, #FFFF00 = yellow (red + green), #FFFFFF = white" )
71 #define FONTSIZER_TEXT N_("Relative font size")
72 #define FONTSIZER_LONGTEXT N_("This is the relative default size of the " \
73   "fonts that will be rendered on the video. If absolute font size is set, "\
74    "relative size will be overriden." )
75
76 static int   pi_sizes[] = { 20, 18, 16, 12, 6 };
77 static char *ppsz_sizes_text[] = { N_("Smaller"), N_("Small"), N_("Normal"),
78                                    N_("Large"), N_("Larger") };
79 static int pi_color_values[] = {
80   0x00000000, 0x00808080, 0x00C0C0C0, 0x00FFFFFF, 0x00800000,
81   0x00FF0000, 0x00FF00FF, 0x00FFFF00, 0x00808000, 0x00008000, 0x00008080,
82   0x0000FF00, 0x00800080, 0x00000080, 0x000000FF, 0x0000FFFF };
83
84 static char *ppsz_color_descriptions[] = {
85   N_("Black"), N_("Gray"), N_("Silver"), N_("White"), N_("Maroon"),
86   N_("Red"), N_("Fuchsia"), N_("Yellow"), N_("Olive"), N_("Green"), N_("Teal"),
87   N_("Lime"), N_("Purple"), N_("Navy"), N_("Blue"), N_("Aqua") };
88
89 vlc_module_begin();
90     set_shortname( _("Text renderer"));
91     set_description( _("Win32 font renderer") );
92     set_category( CAT_VIDEO );
93     set_subcategory( SUBCAT_VIDEO_SUBPIC );
94
95     add_integer( "win32text-fontsize", 0, NULL, FONTSIZE_TEXT,
96                  FONTSIZE_LONGTEXT, VLC_TRUE );
97
98     /* opacity valid on 0..255, with default 255 = fully opaque */
99     add_integer_with_range( "win32-opacity", 255, 0, 255, NULL,
100         OPACITY_TEXT, OPACITY_LONGTEXT, VLC_FALSE );
101
102     /* hook to the color values list, with default 0x00ffffff = white */
103     add_integer( "win32text-color", 0x00FFFFFF, NULL, COLOR_TEXT,
104                  COLOR_LONGTEXT, VLC_TRUE );
105         change_integer_list( pi_color_values, ppsz_color_descriptions, 0 );
106
107     add_integer( "win32text-rel-fontsize", 16, NULL, FONTSIZER_TEXT,
108                  FONTSIZER_LONGTEXT, VLC_FALSE );
109         change_integer_list( pi_sizes, ppsz_sizes_text, 0 );
110
111     set_capability( "text renderer", 50 );
112     add_shortcut( "text" );
113     set_callbacks( Create, Destroy );
114 vlc_module_end();
115
116 /*****************************************************************************
117  * filter_sys_t: win32text local data
118  *****************************************************************************/
119 struct filter_sys_t
120 {
121     uint8_t        i_font_opacity;
122     int            i_font_color;
123     int            i_font_size;
124
125     int            i_default_font_size;
126     int            i_display_height;
127
128     HDC hcdc;
129     HFONT hfont;
130     HFONT hfont_bak;
131     int i_logpy;
132 };
133
134 static uint8_t pi_gamma[16] =
135   {0x00, 0x41, 0x52, 0x63, 0x84, 0x85, 0x96, 0xa7, 0xb8, 0xc9,
136    0xca, 0xdb, 0xdc, 0xed, 0xee, 0xff};
137
138 /*****************************************************************************
139  * Create: creates the module
140  *****************************************************************************/
141 static int Create( vlc_object_t *p_this )
142 {
143     filter_t *p_filter = (filter_t *)p_this;
144     filter_sys_t *p_sys;
145     char *psz_fontfile = NULL;
146     vlc_value_t val;
147     HDC hdc;
148
149     /* Allocate structure */
150     p_filter->p_sys = p_sys = malloc( sizeof( filter_sys_t ) );
151     if( !p_sys )
152     {
153         msg_Err( p_filter, "out of memory" );
154         return VLC_ENOMEM;
155     }
156     p_sys->i_font_size = 0;
157     p_sys->i_display_height = 0;
158
159     var_Create( p_filter, "win32text-font",
160                 VLC_VAR_STRING | VLC_VAR_DOINHERIT );
161     var_Create( p_filter, "win32text-fontsize",
162                 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
163     var_Create( p_filter, "win32text-rel-fontsize",
164                 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
165     var_Create( p_filter, "win32text-opacity",
166                 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
167     var_Get( p_filter, "win32text-opacity", &val );
168     p_sys->i_font_opacity = __MAX( __MIN( val.i_int, 255 ), 0 );
169     var_Create( p_filter, "win32text-color",
170                 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
171     var_Get( p_filter, "win32text-color", &val );
172     p_sys->i_font_color = __MAX( __MIN( val.i_int, 0xFFFFFF ), 0 );
173
174     p_sys->hfont = p_sys->hfont_bak = 0;
175     hdc = GetDC( NULL );
176     p_sys->hcdc = CreateCompatibleDC( hdc );
177     p_sys->i_logpy = GetDeviceCaps( hdc, LOGPIXELSY );
178     ReleaseDC( NULL, hdc );
179     SetBkMode( p_sys->hcdc, TRANSPARENT );
180
181     var_Get( p_filter, "win32text-fontsize", &val );
182     p_sys->i_default_font_size = val.i_int;
183     if( SetFont( p_filter, 0 ) != VLC_SUCCESS ) goto error;
184
185     if( psz_fontfile ) free( psz_fontfile );
186     p_filter->pf_render_text = RenderText;
187     p_filter->pf_render_html = NULL;
188     return VLC_SUCCESS;
189
190  error:
191     if( psz_fontfile ) free( psz_fontfile );
192     free( p_sys );
193     return VLC_EGENERIC;
194 }
195
196 /*****************************************************************************
197  * Destroy: destroy the module
198  *****************************************************************************/
199 static void Destroy( vlc_object_t *p_this )
200 {
201     filter_t *p_filter = (filter_t *)p_this;
202     filter_sys_t *p_sys = p_filter->p_sys;
203
204     if( p_sys->hfont_bak ) SelectObject( p_sys->hcdc, p_sys->hfont_bak );
205     if( p_sys->hfont ) DeleteObject( p_sys->hfont );
206     DeleteDC( p_sys->hcdc );
207     free( p_sys );
208 }
209
210 /*****************************************************************************
211  * Render: place string in picture
212  *****************************************************************************
213  * This function merges the previously rendered win32text glyphs into a picture
214  *****************************************************************************/
215 static int Render( filter_t *p_filter, subpicture_region_t *p_region,
216                    uint8_t *p_bitmap, int i_width, int i_height )
217 {
218     uint8_t *p_dst;
219     video_format_t fmt;
220     int i, i_pitch;
221     subpicture_region_t *p_region_tmp;
222     vlc_bool_t b_outline = VLC_TRUE;
223
224     /* Create a new subpicture region */
225     memset( &fmt, 0, sizeof(video_format_t) );
226     fmt.i_chroma = VLC_FOURCC('Y','U','V','P');
227     fmt.i_width = fmt.i_visible_width = i_width + (b_outline ? 4 : 0);
228     fmt.i_height = fmt.i_visible_height = i_height + (b_outline ? 4 : 0);
229     fmt.i_x_offset = fmt.i_y_offset = 0;
230     p_region_tmp = spu_CreateRegion( p_filter, &fmt );
231     if( !p_region_tmp )
232     {
233         msg_Err( p_filter, "cannot allocate SPU region" );
234         return VLC_EGENERIC;
235     }
236
237     /* Build palette */
238     fmt.p_palette->i_entries = 16;
239     for( i = 0; i < fmt.p_palette->i_entries; i++ )
240     {
241         fmt.p_palette->palette[i][0] = pi_gamma[i];
242         fmt.p_palette->palette[i][1] = 128;
243         fmt.p_palette->palette[i][2] = 128;
244         fmt.p_palette->palette[i][3] = pi_gamma[i];
245     }
246
247     p_region->fmt = p_region_tmp->fmt;
248     p_region->picture = p_region_tmp->picture;
249     free( p_region_tmp );
250
251     p_dst = p_region->picture.Y_PIXELS;
252     i_pitch = p_region->picture.Y_PITCH;
253
254     if( b_outline )
255     {
256         memset( p_dst, 0, i_pitch * fmt.i_height );
257         p_dst += p_region->picture.Y_PITCH * 2 + 2;
258     }
259
260     for( i = 0; i < i_height; i++ )
261     {
262         memcpy( p_dst, p_bitmap, i_width );
263         p_bitmap += (i_width+3) & ~3;
264         p_dst += i_pitch;
265     }
266
267     /* Outlining (find something better than nearest neighbour filtering ?) */
268     if( b_outline )
269     {
270         uint8_t *p_top = p_dst; /* Use 1st line as a cache */
271         uint8_t left, current;
272         int x, y;
273
274         p_dst = p_region->picture.Y_PIXELS;
275
276         for( y = 1; y < (int)fmt.i_height - 1; y++ )
277         {
278             memcpy( p_top, p_dst, fmt.i_width );
279             p_dst += i_pitch;
280             left = 0;
281
282             for( x = 1; x < (int)fmt.i_width - 1; x++ )
283             {
284                 current = p_dst[x];
285                 p_dst[x] = ( 4 * (int)p_dst[x] + left + p_top[x] + p_dst[x+1] +
286                              p_dst[x + i_pitch]) / 8;
287                 left = current;
288             }
289         }
290         memset( p_top, 0, fmt.i_width );
291     }
292
293     return VLC_SUCCESS;
294 }
295
296 static int RenderText( filter_t *p_filter, subpicture_region_t *p_region_out,
297                        subpicture_region_t *p_region_in )
298 {
299     filter_sys_t *p_sys = p_filter->p_sys;
300     int i_font_color, i_font_alpha, i_font_size;
301     uint8_t *p_bitmap;
302     TCHAR *psz_string;
303     int i, i_width, i_height;
304     HBITMAP bitmap, bitmap_bak;
305     BITMAPINFO *p_bmi;
306     RECT rect = {0};
307     SIZE size;
308
309     /* Sanity check */
310     if( !p_region_in || !p_region_out ) return VLC_EGENERIC;
311 #ifdef UNICODE
312     psz_string = malloc( (strlen( p_region_in->psz_text )+1) * sizeof(TCHAR) );
313     if( mbstowcs( psz_string, p_region_in->psz_text,
314                   strlen( p_region_in->psz_text ) * sizeof(TCHAR) ) < 0 )
315     {
316         free( psz_string );
317         return VLC_EGENERIC;
318     }
319 #else
320     psz_string = strdup( p_region_in->psz_text );
321 #endif
322     if( !psz_string || !*psz_string ) return VLC_EGENERIC;
323
324     if( p_region_in->p_style )
325     {
326         i_font_color = __MAX( __MIN( p_region_in->p_style->i_font_color, 0xFFFFFF ), 0 );
327         i_font_alpha = __MAX( __MIN( p_region_in->p_style->i_font_alpha, 255 ), 0 );
328         i_font_size  = __MAX( __MIN( p_region_in->p_style->i_font_size, 255 ), 0 );
329     }
330     else
331     {
332         i_font_color = p_sys->i_font_color;
333         i_font_alpha = 255 - p_sys->i_font_opacity;
334         i_font_size = p_sys->i_default_font_size;
335     }
336
337     SetFont( p_filter, i_font_size );
338
339     SetTextColor( p_sys->hcdc, RGB( (i_font_color >> 16) & 0xff,
340                   (i_font_color >> 8) & 0xff, i_font_color & 0xff) );
341
342     GetTextExtentExPoint( p_sys->hcdc, psz_string, _tcslen(psz_string),
343                           0, 0, 0, &size );
344     i_width = rect.right = size.cx; i_height = rect.bottom = size.cy;
345
346     p_bmi = malloc(sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD)*16);
347     memset( p_bmi, 0, sizeof(BITMAPINFOHEADER) );
348     p_bmi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
349     p_bmi->bmiHeader.biWidth = (i_width+3) & ~3;
350     p_bmi->bmiHeader.biHeight = - i_height;
351     p_bmi->bmiHeader.biPlanes = 1;
352     p_bmi->bmiHeader.biBitCount = 8;
353     p_bmi->bmiHeader.biCompression = BI_RGB;
354     p_bmi->bmiHeader.biClrUsed = 16;
355
356     for( i = 0; i < 16; i++ )
357     {
358         p_bmi->bmiColors[i].rgbBlue =
359             p_bmi->bmiColors[i].rgbGreen =
360                 p_bmi->bmiColors[i].rgbRed = pi_gamma[i];
361     }
362
363     bitmap = CreateDIBSection( p_sys->hcdc, p_bmi, DIB_RGB_COLORS,
364                                (void **)&p_bitmap, NULL, 0 );
365     if( !bitmap )
366     {
367         msg_Err( p_filter, "could not create bitmap" );
368         return VLC_EGENERIC;
369     }
370
371     bitmap_bak = SelectObject( p_sys->hcdc, bitmap );
372     FillRect( p_sys->hcdc, &rect, (HBRUSH)GetStockObject(BLACK_BRUSH) );
373
374     //TextOut( p_sys->hcdc, 0, 0, psz_string, strlen(psz_string) );
375     if( !DrawText( p_sys->hcdc, psz_string, -1, &rect, 0 ) )
376     {
377         msg_Err( p_filter, "could not draw text" );
378     }
379
380     p_region_out->i_x = p_region_in->i_x;
381     p_region_out->i_y = p_region_in->i_y;
382     Render( p_filter, p_region_out, p_bitmap, i_width, i_height );
383
384     SelectObject( p_sys->hcdc, bitmap_bak );
385     DeleteObject( bitmap );
386     return VLC_SUCCESS;
387 }
388
389 static int SetFont( filter_t *p_filter, int i_size )
390 {
391     filter_sys_t *p_sys = p_filter->p_sys;
392     LOGFONT logfont;
393
394     if( i_size && i_size == p_sys->i_font_size ) return VLC_SUCCESS;
395
396     if( !i_size )
397     {
398         vlc_value_t val;
399
400         if( !p_sys->i_default_font_size &&
401             p_sys->i_display_height == (int)p_filter->fmt_out.video.i_height )
402             return VLC_SUCCESS;
403
404         if( p_sys->i_default_font_size )
405         {
406             i_size = p_sys->i_default_font_size;
407         }
408         else
409         {
410             var_Get( p_filter, "win32text-rel-fontsize", &val );
411             i_size = (int)p_filter->fmt_out.video.i_height / val.i_int;
412             p_filter->p_sys->i_display_height =
413                 p_filter->fmt_out.video.i_height;
414         }
415         if( i_size <= 0 )
416         {
417             msg_Warn( p_filter, "invalid fontsize, using 12" );
418             i_size = 12;
419         }
420
421         msg_Dbg( p_filter, "using fontsize: %i", i_size );
422     }
423
424     p_sys->i_font_size = i_size;
425
426     if( p_sys->hfont_bak ) SelectObject( p_sys->hcdc, p_sys->hfont_bak );
427     if( p_sys->hfont ) DeleteObject( p_sys->hfont );
428
429     i_size = i_size * (int64_t)p_sys->i_logpy / 72;
430
431     logfont.lfHeight = i_size;
432     logfont.lfWidth = 0;
433     logfont.lfEscapement = 0;
434     logfont.lfOrientation = 0;
435     logfont.lfWeight = 0;
436     logfont.lfItalic = FALSE;
437     logfont.lfUnderline = FALSE;
438     logfont.lfStrikeOut = FALSE;
439     logfont.lfCharSet = ANSI_CHARSET;
440     logfont.lfOutPrecision = OUT_DEFAULT_PRECIS;
441     logfont.lfClipPrecision = CLIP_DEFAULT_PRECIS;
442     logfont.lfQuality = ANTIALIASED_QUALITY;
443     logfont.lfPitchAndFamily = DEFAULT_PITCH;
444     memcpy( logfont.lfFaceName, _T("Arial"), sizeof(_T("Arial")) );
445
446     p_sys->hfont = CreateFontIndirect( &logfont );
447
448     p_sys->hfont_bak = SelectObject( p_sys->hcdc, p_sys->hfont );
449
450     return VLC_SUCCESS;
451 }