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