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