]> git.sesse.net Git - vlc/blob - modules/misc/win32text.c
Use var_Inherit* instead of var_CreateGet*.
[vlc] / modules / misc / win32text.c
1 /*****************************************************************************
2  * win32text.c : Text drawing routines using the TextOut win32 API
3  *****************************************************************************
4  * Copyright (C) 2002 - 2009 the VideoLAN team
5  * $Id$
6  *
7  * Authors: Gildas Bazin <gbazin@videolan.org>
8  *          Pierre Ynard
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., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23  *****************************************************************************/
24
25 /*****************************************************************************
26  * Preamble
27  *****************************************************************************/
28
29 #ifdef HAVE_CONFIG_H
30 # include "config.h"
31 #endif
32
33 #include <vlc_common.h>
34 #include <vlc_plugin.h>
35 #include <vlc_vout.h>
36 #include <vlc_osd.h>
37 #include <vlc_block.h>
38 #include <vlc_filter.h>
39
40 #include <math.h>
41
42 /*****************************************************************************
43  * Local prototypes
44  *****************************************************************************/
45 static int  Create ( vlc_object_t * );
46 static void Destroy( vlc_object_t * );
47
48 /* The RenderText call maps to pf_render_string, defined in vlc_filter.h */
49 static int RenderText( filter_t *, subpicture_region_t *,
50                        subpicture_region_t * );
51
52 static int Render( filter_t *, subpicture_region_t *, uint8_t *, int, int);
53 static int SetFont( filter_t *, int );
54
55 /*****************************************************************************
56  * Module descriptor
57  *****************************************************************************/
58 #define FONT_TEXT N_("Font")
59 #define FONT_LONGTEXT N_("Filename for the font you want to use")
60 #define FONTSIZE_TEXT N_("Font size in pixels")
61 #define FONTSIZE_LONGTEXT N_("This is the default size of the fonts " \
62      "that will be rendered on the video. " \
63      "If set to something different than 0 this option will override the " \
64      "relative font size." )
65 #define OPACITY_TEXT N_("Opacity")
66 #define OPACITY_LONGTEXT N_("The opacity (inverse of transparency) of the " \
67      "text that will be rendered on the video. 0 = transparent, " \
68      "255 = totally opaque. " )
69 #define COLOR_TEXT N_("Text default color")
70 #define COLOR_LONGTEXT N_("The color of the text that will be rendered on "\
71   "the video. This must be an hexadecimal (like HTML colors). The first two "\
72   "chars are for red, then green, then blue. #000000 = black, #FF0000 = red,"\
73   " #00FF00 = green, #FFFF00 = yellow (red + green), #FFFFFF = white" )
74 #define FONTSIZER_TEXT N_("Relative font size")
75 #define FONTSIZER_LONGTEXT N_("This is the relative default size of the " \
76   "fonts that will be rendered on the video. If absolute font size is set, "\
77    "relative size will be overriden." )
78
79 static int const pi_sizes[] = { 20, 18, 16, 12, 6 };
80 static char *const ppsz_sizes_text[] = {
81     N_("Smaller"), N_("Small"), N_("Normal"), N_("Large"), N_("Larger") };
82 static const int pi_color_values[] = {
83   0x00000000, 0x00808080, 0x00C0C0C0, 0x00FFFFFF, 0x00800000,
84   0x00FF0000, 0x00FF00FF, 0x00FFFF00, 0x00808000, 0x00008000, 0x00008080,
85   0x0000FF00, 0x00800080, 0x00000080, 0x000000FF, 0x0000FFFF };
86
87 static const char *const ppsz_color_descriptions[] = {
88   N_("Black"), N_("Gray"), N_("Silver"), N_("White"), N_("Maroon"),
89   N_("Red"), N_("Fuchsia"), N_("Yellow"), N_("Olive"), N_("Green"), N_("Teal"),
90   N_("Lime"), N_("Purple"), N_("Navy"), N_("Blue"), N_("Aqua") };
91
92 vlc_module_begin ()
93     set_shortname( N_("Text renderer"))
94     set_description( N_("Win32 font renderer") )
95     set_category( CAT_VIDEO )
96     set_subcategory( SUBCAT_VIDEO_SUBPIC )
97
98     add_integer( "win32text-fontsize", 0, NULL, FONTSIZE_TEXT,
99                  FONTSIZE_LONGTEXT, true )
100
101     /* opacity valid on 0..255, with default 255 = fully opaque */
102     add_integer_with_range( "win32-opacity", 255, 0, 255, NULL,
103         OPACITY_TEXT, OPACITY_LONGTEXT, false )
104
105     /* hook to the color values list, with default 0x00ffffff = white */
106     add_integer( "win32text-color", 0x00FFFFFF, NULL, COLOR_TEXT,
107                  COLOR_LONGTEXT, true )
108         change_integer_list( pi_color_values, ppsz_color_descriptions, NULL );
109
110     add_integer( "win32text-rel-fontsize", 16, NULL, FONTSIZER_TEXT,
111                  FONTSIZER_LONGTEXT, false )
112         change_integer_list( pi_sizes, ppsz_sizes_text, NULL );
113
114     set_capability( "text renderer", 50 )
115     add_shortcut( "text" )
116     set_callbacks( Create, Destroy )
117 vlc_module_end ()
118
119 /*****************************************************************************
120  * filter_sys_t: win32text local data
121  *****************************************************************************/
122 struct filter_sys_t
123 {
124     uint8_t        i_font_opacity;
125     int            i_font_color;
126     int            i_font_size;
127
128     int            i_default_font_size;
129     int            i_display_height;
130
131     HDC hcdc;
132     HFONT hfont;
133     HFONT hfont_bak;
134     int i_logpy;
135 };
136
137 static const uint8_t pi_gamma[16] =
138   {0x00, 0x41, 0x52, 0x63, 0x84, 0x85, 0x96, 0xa7, 0xb8, 0xc9,
139    0xca, 0xdb, 0xdc, 0xed, 0xee, 0xff};
140
141 /*****************************************************************************
142  * Create: creates the module
143  *****************************************************************************/
144 static int Create( vlc_object_t *p_this )
145 {
146     filter_t *p_filter = (filter_t *)p_this;
147     filter_sys_t *p_sys;
148     char *psz_fontfile = NULL;
149     vlc_value_t val;
150     HDC hdc;
151
152     /* Allocate structure */
153     p_filter->p_sys = p_sys = malloc( sizeof( filter_sys_t ) );
154     if( !p_sys )
155         return VLC_ENOMEM;
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     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     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     bool b_outline = true;
222
223     /* Create a new subpicture region */
224     memset( &fmt, 0, sizeof(video_format_t) );
225     fmt.i_chroma = VLC_CODEC_YUVP;
226     fmt.i_width = fmt.i_visible_width = i_width + (b_outline ? 4 : 0);
227     fmt.i_height = fmt.i_visible_height = i_height + (b_outline ? 4 : 0);
228     fmt.i_x_offset = fmt.i_y_offset = 0;
229
230     /* Build palette */
231     fmt.p_palette = calloc( 1, sizeof(*fmt.p_palette) );
232     if( !fmt.p_palette )
233         return VLC_EGENERIC;
234     fmt.p_palette->i_entries = 16;
235     for( i = 0; i < fmt.p_palette->i_entries; i++ )
236     {
237         fmt.p_palette->palette[i][0] = pi_gamma[i];
238         fmt.p_palette->palette[i][1] = 128;
239         fmt.p_palette->palette[i][2] = 128;
240         fmt.p_palette->palette[i][3] = pi_gamma[i];
241     }
242
243     p_region->p_picture = picture_NewFromFormat( &fmt );
244     if( !p_region->p_picture )
245     {
246         free( fmt.p_palette );
247         return VLC_EGENERIC;
248     }
249     p_region->fmt = fmt;
250
251     p_dst = p_region->p_picture->Y_PIXELS;
252     i_pitch = p_region->p_picture->Y_PITCH;
253
254     if( b_outline )
255     {
256         memset( p_dst, 0, i_pitch * fmt.i_height );
257         p_dst += p_region->p_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->p_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, 0, 0, 0 };
307
308     /* Sanity check */
309     if( !p_region_in || !p_region_out ) return VLC_EGENERIC;
310     if( !p_region_in->psz_text || !*p_region_in->psz_text )
311         return VLC_EGENERIC;
312
313     psz_string = malloc( (strlen( p_region_in->psz_text )+1) * sizeof(TCHAR) );
314     if( !psz_string )
315         return VLC_ENOMEM;
316 #ifdef UNICODE
317     if( mbstowcs( psz_string, p_region_in->psz_text,
318                   strlen( p_region_in->psz_text ) * sizeof(TCHAR) ) < 0 )
319     {
320         free( psz_string );
321         return VLC_EGENERIC;
322     }
323 #else
324     strcpy( psz_string, p_region_in->psz_text );
325 #endif
326     if( !*psz_string )
327     {
328         free( psz_string );
329         return VLC_EGENERIC;
330     }
331
332     if( p_region_in->p_style )
333     {
334         i_font_color = __MAX( __MIN( p_region_in->p_style->i_font_color, 0xFFFFFF ), 0 );
335         i_font_alpha = __MAX( __MIN( p_region_in->p_style->i_font_alpha, 255 ), 0 );
336         i_font_size  = __MAX( __MIN( p_region_in->p_style->i_font_size, 255 ), 0 );
337     }
338     else
339     {
340         i_font_color = p_sys->i_font_color;
341         i_font_alpha = 255 - p_sys->i_font_opacity;
342         i_font_size = p_sys->i_default_font_size;
343     }
344
345     SetFont( p_filter, i_font_size );
346
347     SetTextColor( p_sys->hcdc, RGB( (i_font_color >> 16) & 0xff,
348                   (i_font_color >> 8) & 0xff, i_font_color & 0xff) );
349
350     DrawText( p_sys->hcdc, psz_string, -1, &rect,
351               DT_CALCRECT | DT_CENTER | DT_NOPREFIX );
352     i_width = rect.right; i_height = rect.bottom;
353
354     p_bmi = malloc(sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD)*16);
355     memset( p_bmi, 0, sizeof(BITMAPINFOHEADER) );
356     p_bmi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
357     p_bmi->bmiHeader.biWidth = (i_width+3) & ~3;
358     p_bmi->bmiHeader.biHeight = - i_height;
359     p_bmi->bmiHeader.biPlanes = 1;
360     p_bmi->bmiHeader.biBitCount = 8;
361     p_bmi->bmiHeader.biCompression = BI_RGB;
362     p_bmi->bmiHeader.biClrUsed = 16;
363
364     for( i = 0; i < 16; i++ )
365     {
366         p_bmi->bmiColors[i].rgbBlue =
367             p_bmi->bmiColors[i].rgbGreen =
368                 p_bmi->bmiColors[i].rgbRed = pi_gamma[i];
369     }
370
371     bitmap = CreateDIBSection( p_sys->hcdc, p_bmi, DIB_RGB_COLORS,
372                                (void **)&p_bitmap, NULL, 0 );
373     if( !bitmap )
374     {
375         msg_Err( p_filter, "could not create bitmap" );
376         free( psz_string );
377         return VLC_EGENERIC;
378     }
379
380     bitmap_bak = SelectObject( p_sys->hcdc, bitmap );
381     FillRect( p_sys->hcdc, &rect, (HBRUSH)GetStockObject(BLACK_BRUSH) );
382
383     if( !DrawText( p_sys->hcdc, psz_string, -1, &rect,
384                    DT_CENTER | DT_NOPREFIX ) )
385     {
386         msg_Err( p_filter, "could not draw text" );
387     }
388
389     p_region_out->i_x = p_region_in->i_x;
390     p_region_out->i_y = p_region_in->i_y;
391     Render( p_filter, p_region_out, p_bitmap, i_width, i_height );
392
393     SelectObject( p_sys->hcdc, bitmap_bak );
394     DeleteObject( bitmap );
395     free( psz_string );
396     return VLC_SUCCESS;
397 }
398
399 static int SetFont( filter_t *p_filter, int i_size )
400 {
401     filter_sys_t *p_sys = p_filter->p_sys;
402     LOGFONT logfont;
403
404     if( i_size && i_size == p_sys->i_font_size ) return VLC_SUCCESS;
405
406     if( !i_size )
407     {
408         vlc_value_t val;
409
410         if( !p_sys->i_default_font_size &&
411             p_sys->i_display_height == (int)p_filter->fmt_out.video.i_height )
412             return VLC_SUCCESS;
413
414         if( p_sys->i_default_font_size )
415         {
416             i_size = p_sys->i_default_font_size;
417         }
418         else
419         {
420             var_Get( p_filter, "win32text-rel-fontsize", &val );
421             i_size = (int)p_filter->fmt_out.video.i_height / val.i_int;
422             p_filter->p_sys->i_display_height =
423                 p_filter->fmt_out.video.i_height;
424         }
425         if( i_size <= 0 )
426         {
427             msg_Warn( p_filter, "invalid fontsize, using 12" );
428             i_size = 12;
429         }
430
431         msg_Dbg( p_filter, "using fontsize: %i", i_size );
432     }
433
434     p_sys->i_font_size = i_size;
435
436     if( p_sys->hfont_bak ) SelectObject( p_sys->hcdc, p_sys->hfont_bak );
437     if( p_sys->hfont ) DeleteObject( p_sys->hfont );
438
439     i_size = i_size * (int64_t)p_sys->i_logpy / 72;
440
441     logfont.lfHeight = i_size;
442     logfont.lfWidth = 0;
443     logfont.lfEscapement = 0;
444     logfont.lfOrientation = 0;
445     logfont.lfWeight = 0;
446     logfont.lfItalic = FALSE;
447     logfont.lfUnderline = FALSE;
448     logfont.lfStrikeOut = FALSE;
449     logfont.lfCharSet = ANSI_CHARSET;
450     logfont.lfOutPrecision = OUT_DEFAULT_PRECIS;
451     logfont.lfClipPrecision = CLIP_DEFAULT_PRECIS;
452     logfont.lfQuality = ANTIALIASED_QUALITY;
453     logfont.lfPitchAndFamily = DEFAULT_PITCH;
454     memcpy( logfont.lfFaceName, _T("Arial"), sizeof(_T("Arial")) );
455
456     p_sys->hfont = CreateFontIndirect( &logfont );
457
458     p_sys->hfont_bak = SelectObject( p_sys->hcdc, p_sys->hfont );
459
460     return VLC_SUCCESS;
461 }