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