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