]> git.sesse.net Git - vlc/blob - modules/misc/win32text.c
9c16c12c70e58c581d0905fa3481176591969b23
[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, NULL );
108
109     add_integer( "win32text-rel-fontsize", 16, NULL, FONTSIZER_TEXT,
110                  FONTSIZER_LONGTEXT, false );
111         change_integer_list( pi_sizes, ppsz_sizes_text, NULL );
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 const 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     bool b_outline = true;
221
222     /* Create a new subpicture region */
223     memset( &fmt, 0, sizeof(video_format_t) );
224     fmt.i_chroma = VLC_FOURCC('Y','U','V','P');
225     fmt.i_width = fmt.i_visible_width = i_width + (b_outline ? 4 : 0);
226     fmt.i_height = fmt.i_visible_height = i_height + (b_outline ? 4 : 0);
227     fmt.i_x_offset = fmt.i_y_offset = 0;
228
229     /* Build palette */
230     fmt.p_palette->i_entries = 16;
231     for( i = 0; i < fmt.p_palette->i_entries; i++ )
232     {
233         fmt.p_palette->palette[i][0] = pi_gamma[i];
234         fmt.p_palette->palette[i][1] = 128;
235         fmt.p_palette->palette[i][2] = 128;
236         fmt.p_palette->palette[i][3] = pi_gamma[i];
237     }
238
239     p_region->p_picture = picture_New( fmt.i_chroma, fmt.i_width, fmt.i_height, fmt.i_aspect );
240     if( !p_region->p_picture )
241         return VLC_EGENERIC;
242     p_region->fmt = fmt;
243
244     p_dst = p_region->p_picture->Y_PIXELS;
245     i_pitch = p_region->p_picture->Y_PITCH;
246
247     if( b_outline )
248     {
249         memset( p_dst, 0, i_pitch * fmt.i_height );
250         p_dst += p_region->p_picture->Y_PITCH * 2 + 2;
251     }
252
253     for( i = 0; i < i_height; i++ )
254     {
255         memcpy( p_dst, p_bitmap, i_width );
256         p_bitmap += (i_width+3) & ~3;
257         p_dst += i_pitch;
258     }
259
260     /* Outlining (find something better than nearest neighbour filtering ?) */
261     if( b_outline )
262     {
263         uint8_t *p_top = p_dst; /* Use 1st line as a cache */
264         uint8_t left, current;
265         int x, y;
266
267         p_dst = p_region->p_picture->Y_PIXELS;
268
269         for( y = 1; y < (int)fmt.i_height - 1; y++ )
270         {
271             memcpy( p_top, p_dst, fmt.i_width );
272             p_dst += i_pitch;
273             left = 0;
274
275             for( x = 1; x < (int)fmt.i_width - 1; x++ )
276             {
277                 current = p_dst[x];
278                 p_dst[x] = ( 4 * (int)p_dst[x] + left + p_top[x] + p_dst[x+1] +
279                              p_dst[x + i_pitch]) / 8;
280                 left = current;
281             }
282         }
283         memset( p_top, 0, fmt.i_width );
284     }
285
286     return VLC_SUCCESS;
287 }
288
289 static int RenderText( filter_t *p_filter, subpicture_region_t *p_region_out,
290                        subpicture_region_t *p_region_in )
291 {
292     filter_sys_t *p_sys = p_filter->p_sys;
293     int i_font_color, i_font_alpha, i_font_size;
294     uint8_t *p_bitmap;
295     TCHAR *psz_string;
296     int i, i_width, i_height;
297     HBITMAP bitmap, bitmap_bak;
298     BITMAPINFO *p_bmi;
299     RECT rect = {0};
300     SIZE size;
301
302     /* Sanity check */
303     if( !p_region_in || !p_region_out ) return VLC_EGENERIC;
304 #ifdef UNICODE
305     psz_string = malloc( (strlen( p_region_in->psz_text )+1) * sizeof(TCHAR) );
306     if( mbstowcs( psz_string, p_region_in->psz_text,
307                   strlen( p_region_in->psz_text ) * sizeof(TCHAR) ) < 0 )
308     {
309         free( psz_string );
310         return VLC_EGENERIC;
311     }
312 #else
313     psz_string = strdup( p_region_in->psz_text );
314 #endif
315     if( !psz_string || !*psz_string ) return VLC_EGENERIC;
316
317     if( p_region_in->p_style )
318     {
319         i_font_color = __MAX( __MIN( p_region_in->p_style->i_font_color, 0xFFFFFF ), 0 );
320         i_font_alpha = __MAX( __MIN( p_region_in->p_style->i_font_alpha, 255 ), 0 );
321         i_font_size  = __MAX( __MIN( p_region_in->p_style->i_font_size, 255 ), 0 );
322     }
323     else
324     {
325         i_font_color = p_sys->i_font_color;
326         i_font_alpha = 255 - p_sys->i_font_opacity;
327         i_font_size = p_sys->i_default_font_size;
328     }
329
330     SetFont( p_filter, i_font_size );
331
332     SetTextColor( p_sys->hcdc, RGB( (i_font_color >> 16) & 0xff,
333                   (i_font_color >> 8) & 0xff, i_font_color & 0xff) );
334
335     GetTextExtentExPoint( p_sys->hcdc, psz_string, _tcslen(psz_string),
336                           0, 0, 0, &size );
337     i_width = rect.right = size.cx; i_height = rect.bottom = size.cy;
338
339     p_bmi = malloc(sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD)*16);
340     memset( p_bmi, 0, sizeof(BITMAPINFOHEADER) );
341     p_bmi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
342     p_bmi->bmiHeader.biWidth = (i_width+3) & ~3;
343     p_bmi->bmiHeader.biHeight = - i_height;
344     p_bmi->bmiHeader.biPlanes = 1;
345     p_bmi->bmiHeader.biBitCount = 8;
346     p_bmi->bmiHeader.biCompression = BI_RGB;
347     p_bmi->bmiHeader.biClrUsed = 16;
348
349     for( i = 0; i < 16; i++ )
350     {
351         p_bmi->bmiColors[i].rgbBlue =
352             p_bmi->bmiColors[i].rgbGreen =
353                 p_bmi->bmiColors[i].rgbRed = pi_gamma[i];
354     }
355
356     bitmap = CreateDIBSection( p_sys->hcdc, p_bmi, DIB_RGB_COLORS,
357                                (void **)&p_bitmap, NULL, 0 );
358     if( !bitmap )
359     {
360         msg_Err( p_filter, "could not create bitmap" );
361         return VLC_EGENERIC;
362     }
363
364     bitmap_bak = SelectObject( p_sys->hcdc, bitmap );
365     FillRect( p_sys->hcdc, &rect, (HBRUSH)GetStockObject(BLACK_BRUSH) );
366
367     //TextOut( p_sys->hcdc, 0, 0, psz_string, strlen(psz_string) );
368     if( !DrawText( p_sys->hcdc, psz_string, -1, &rect, 0 ) )
369     {
370         msg_Err( p_filter, "could not draw text" );
371     }
372
373     p_region_out->i_x = p_region_in->i_x;
374     p_region_out->i_y = p_region_in->i_y;
375     Render( p_filter, p_region_out, p_bitmap, i_width, i_height );
376
377     SelectObject( p_sys->hcdc, bitmap_bak );
378     DeleteObject( bitmap );
379     return VLC_SUCCESS;
380 }
381
382 static int SetFont( filter_t *p_filter, int i_size )
383 {
384     filter_sys_t *p_sys = p_filter->p_sys;
385     LOGFONT logfont;
386
387     if( i_size && i_size == p_sys->i_font_size ) return VLC_SUCCESS;
388
389     if( !i_size )
390     {
391         vlc_value_t val;
392
393         if( !p_sys->i_default_font_size &&
394             p_sys->i_display_height == (int)p_filter->fmt_out.video.i_height )
395             return VLC_SUCCESS;
396
397         if( p_sys->i_default_font_size )
398         {
399             i_size = p_sys->i_default_font_size;
400         }
401         else
402         {
403             var_Get( p_filter, "win32text-rel-fontsize", &val );
404             i_size = (int)p_filter->fmt_out.video.i_height / val.i_int;
405             p_filter->p_sys->i_display_height =
406                 p_filter->fmt_out.video.i_height;
407         }
408         if( i_size <= 0 )
409         {
410             msg_Warn( p_filter, "invalid fontsize, using 12" );
411             i_size = 12;
412         }
413
414         msg_Dbg( p_filter, "using fontsize: %i", i_size );
415     }
416
417     p_sys->i_font_size = i_size;
418
419     if( p_sys->hfont_bak ) SelectObject( p_sys->hcdc, p_sys->hfont_bak );
420     if( p_sys->hfont ) DeleteObject( p_sys->hfont );
421
422     i_size = i_size * (int64_t)p_sys->i_logpy / 72;
423
424     logfont.lfHeight = i_size;
425     logfont.lfWidth = 0;
426     logfont.lfEscapement = 0;
427     logfont.lfOrientation = 0;
428     logfont.lfWeight = 0;
429     logfont.lfItalic = FALSE;
430     logfont.lfUnderline = FALSE;
431     logfont.lfStrikeOut = FALSE;
432     logfont.lfCharSet = ANSI_CHARSET;
433     logfont.lfOutPrecision = OUT_DEFAULT_PRECIS;
434     logfont.lfClipPrecision = CLIP_DEFAULT_PRECIS;
435     logfont.lfQuality = ANTIALIASED_QUALITY;
436     logfont.lfPitchAndFamily = DEFAULT_PITCH;
437     memcpy( logfont.lfFaceName, _T("Arial"), sizeof(_T("Arial")) );
438
439     p_sys->hfont = CreateFontIndirect( &logfont );
440
441     p_sys->hfont_bak = SelectObject( p_sys->hcdc, p_sys->hfont );
442
443     return VLC_SUCCESS;
444 }