]> git.sesse.net Git - vlc/blob - modules/misc/win32text.c
Finish strings review in misc/ (Refs:#438)
[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 #include <stdlib.h>                                      /* malloc(), free() */
28 #include <string.h>
29
30 #include <vlc/vlc.h>
31 #include <vlc/vout.h>
32 #include "vlc_osd.h"
33 #include "vlc_block.h"
34 #include "vlc_filter.h"
35
36 #include <math.h>
37
38 /*****************************************************************************
39  * Local prototypes
40  *****************************************************************************/
41 static int  Create ( vlc_object_t * );
42 static void Destroy( vlc_object_t * );
43
44 /* The RenderText call maps to pf_render_string, defined in vlc_filter.h */
45 static int RenderText( filter_t *, subpicture_region_t *,
46                        subpicture_region_t * );
47
48 static int Render( filter_t *, subpicture_region_t *, uint8_t *, int, int);
49 static int SetFont( filter_t *, int );
50
51 /*****************************************************************************
52  * Module descriptor
53  *****************************************************************************/
54 #define FONT_TEXT N_("Font")
55 #define FONT_LONGTEXT N_("Filename for the font you want to use")
56 #define FONTSIZE_TEXT N_("Font size in pixels")
57 #define FONTSIZE_LONGTEXT N_("This is the default size of the fonts " \
58      "that will be rendered on the video. " \
59      "If set to something different than 0 this option will override the " \
60      "relative font size. " )
61 #define OPACITY_TEXT N_("Opacity")
62 #define OPACITY_LONGTEXT N_("The opacity (inverse of transparency) of the " \
63      "text that will be rendered on the video. 0 = transparent, " \
64      "255 = totally opaque. " )
65 #define COLOR_TEXT N_("Text default color")
66 #define COLOR_LONGTEXT N_("The color of the text that will be rendered on "\
67   "the video. This must be an hexadecimal (like HTML colors). The first two "\    "chars are for red, then green, then blue. #000000 = black, #FF0000 = red,"\    " #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     return VLC_SUCCESS;
185
186  error:
187     if( psz_fontfile ) free( psz_fontfile );
188     free( p_sys );
189     return VLC_EGENERIC;
190 }
191
192 /*****************************************************************************
193  * Destroy: destroy the module
194  *****************************************************************************/
195 static void Destroy( vlc_object_t *p_this )
196 {
197     filter_t *p_filter = (filter_t *)p_this;
198     filter_sys_t *p_sys = p_filter->p_sys;
199
200     if( p_sys->hfont_bak ) SelectObject( p_sys->hcdc, p_sys->hfont_bak );
201     if( p_sys->hfont ) DeleteObject( p_sys->hfont );
202     DeleteDC( p_sys->hcdc );
203     free( p_sys );
204 }
205
206 /*****************************************************************************
207  * Render: place string in picture
208  *****************************************************************************
209  * This function merges the previously rendered win32text glyphs into a picture
210  *****************************************************************************/
211 static int Render( filter_t *p_filter, subpicture_region_t *p_region,
212                    uint8_t *p_bitmap, int i_width, int i_height )
213 {
214     uint8_t *p_dst;
215     video_format_t fmt;
216     int i, i_pitch;
217     subpicture_region_t *p_region_tmp;
218     vlc_bool_t b_outline = VLC_TRUE;
219
220     /* Create a new subpicture region */
221     memset( &fmt, 0, sizeof(video_format_t) );
222     fmt.i_chroma = VLC_FOURCC('Y','U','V','P');
223     fmt.i_width = fmt.i_visible_width = i_width + (b_outline ? 4 : 0);
224     fmt.i_height = fmt.i_visible_height = i_height + (b_outline ? 4 : 0);
225     fmt.i_x_offset = fmt.i_y_offset = 0;
226     p_region_tmp = spu_CreateRegion( p_filter, &fmt );
227     if( !p_region_tmp )
228     {
229         msg_Err( p_filter, "cannot allocate SPU region" );
230         return VLC_EGENERIC;
231     }
232
233     /* Build palette */
234     fmt.p_palette->i_entries = 16;
235     for( i = 0; i < fmt.p_palette->i_entries; i++ )
236     {
237         fmt.p_palette->palette[i][0] = pi_gamma[i];
238         fmt.p_palette->palette[i][1] = 128;
239         fmt.p_palette->palette[i][2] = 128;
240         fmt.p_palette->palette[i][3] = pi_gamma[i];
241     }
242
243     p_region->fmt = p_region_tmp->fmt;
244     p_region->picture = p_region_tmp->picture;
245     free( p_region_tmp );
246
247     p_dst = p_region->picture.Y_PIXELS;
248     i_pitch = p_region->picture.Y_PITCH;
249
250     if( b_outline )
251     {
252         memset( p_dst, 0, i_pitch * fmt.i_height );
253         p_dst += p_region->picture.Y_PITCH * 2 + 2;
254     }
255
256     for( i = 0; i < i_height; i++ )
257     {
258         memcpy( p_dst, p_bitmap, i_width );
259         p_bitmap += (i_width+3) & ~3;
260         p_dst += i_pitch;
261     }
262
263     /* Outlining (find something better than nearest neighbour filtering ?) */
264     if( b_outline )
265     {
266         uint8_t *p_top = p_dst; /* Use 1st line as a cache */
267         uint8_t left, current;
268         int x, y;
269
270         p_dst = p_region->picture.Y_PIXELS;
271
272         for( y = 1; y < (int)fmt.i_height - 1; y++ )
273         {
274             memcpy( p_top, p_dst, fmt.i_width );
275             p_dst += i_pitch;
276             left = 0;
277
278             for( x = 1; x < (int)fmt.i_width - 1; x++ )
279             {
280                 current = p_dst[x];
281                 p_dst[x] = ( 4 * (int)p_dst[x] + left + p_top[x] + p_dst[x+1] +
282                              p_dst[x + i_pitch]) / 8;
283                 left = current;
284             }
285         }
286         memset( p_top, 0, fmt.i_width );
287     }
288
289     return VLC_SUCCESS;
290 }
291
292 static int RenderText( filter_t *p_filter, subpicture_region_t *p_region_out,
293                        subpicture_region_t *p_region_in )
294 {
295     filter_sys_t *p_sys = p_filter->p_sys;
296     int i_font_color, i_font_alpha, i_font_size;
297     uint8_t *p_bitmap;
298     TCHAR *psz_string;
299     int i, i_width, i_height;
300     HBITMAP bitmap, bitmap_bak;
301     BITMAPINFO *p_bmi;
302     RECT rect = {0};
303     SIZE size;
304
305     /* Sanity check */
306     if( !p_region_in || !p_region_out ) return VLC_EGENERIC;
307 #ifdef UNICODE
308     psz_string = malloc( (strlen( p_region_in->psz_text )+1) * sizeof(TCHAR) );
309     if( mbstowcs( psz_string, p_region_in->psz_text,
310                   strlen( p_region_in->psz_text ) * sizeof(TCHAR) ) < 0 )
311     {
312         free( psz_string );
313         return VLC_EGENERIC;
314     }
315 #else
316     psz_string = strdup( p_region_in->psz_text );
317 #endif
318     if( !psz_string || !*psz_string ) return VLC_EGENERIC;
319
320     if( p_region_in->p_style )
321     {
322         i_font_color = __MAX( __MIN( p_region_in->p_style->i_font_color, 0xFFFFFF ), 0 );
323         i_font_alpha = __MAX( __MIN( p_region_in->p_style->i_font_alpha, 255 ), 0 );
324         i_font_size  = __MAX( __MIN( p_region_in->p_style->i_font_size, 255 ), 0 );
325     }
326     else
327     {
328         i_font_color = p_sys->i_font_color;
329         i_font_alpha = 255 - p_sys->i_font_opacity;
330         i_font_size = p_sys->i_default_font_size;
331     }
332
333     SetFont( p_filter, i_font_size );
334
335     SetTextColor( p_sys->hcdc, RGB( (i_font_color >> 16) & 0xff,
336                   (i_font_color >> 8) & 0xff, i_font_color & 0xff) );
337
338     GetTextExtentExPoint( p_sys->hcdc, psz_string, _tcslen(psz_string),
339                           0, 0, 0, &size );
340     i_width = rect.right = size.cx; i_height = rect.bottom = size.cy;
341
342     p_bmi = malloc(sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD)*16);
343     memset( p_bmi, 0, sizeof(BITMAPINFOHEADER) );
344     p_bmi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
345     p_bmi->bmiHeader.biWidth = (i_width+3) & ~3;
346     p_bmi->bmiHeader.biHeight = - i_height;
347     p_bmi->bmiHeader.biPlanes = 1;
348     p_bmi->bmiHeader.biBitCount = 8;
349     p_bmi->bmiHeader.biCompression = BI_RGB;
350     p_bmi->bmiHeader.biClrUsed = 16;
351
352     for( i = 0; i < 16; i++ )
353     {
354         p_bmi->bmiColors[i].rgbBlue =
355             p_bmi->bmiColors[i].rgbGreen =
356                 p_bmi->bmiColors[i].rgbRed = pi_gamma[i];
357     }
358
359     bitmap = CreateDIBSection( p_sys->hcdc, p_bmi, DIB_RGB_COLORS,
360                                (void **)&p_bitmap, NULL, 0 );
361     if( !bitmap )
362     {
363         msg_Err( p_filter, "could not create bitmap" );
364         return VLC_EGENERIC;
365     }
366
367     bitmap_bak = SelectObject( p_sys->hcdc, bitmap );
368     FillRect( p_sys->hcdc, &rect, (HBRUSH)GetStockObject(BLACK_BRUSH) );
369
370     //TextOut( p_sys->hcdc, 0, 0, psz_string, strlen(psz_string) );
371     if( !DrawText( p_sys->hcdc, psz_string, -1, &rect, 0 ) )
372     {
373         msg_Err( p_filter, "could not draw text" );
374     }
375
376     p_region_out->i_x = p_region_in->i_x;
377     p_region_out->i_y = p_region_in->i_y;
378     Render( p_filter, p_region_out, p_bitmap, i_width, i_height );
379
380     SelectObject( p_sys->hcdc, bitmap_bak );
381     DeleteObject( bitmap );
382     return VLC_SUCCESS;
383 }
384
385 static int SetFont( filter_t *p_filter, int i_size )
386 {
387     filter_sys_t *p_sys = p_filter->p_sys;
388     LOGFONT logfont;
389
390     if( i_size && i_size == p_sys->i_font_size ) return VLC_SUCCESS;
391
392     if( !i_size )
393     {
394         vlc_value_t val;
395
396         if( !p_sys->i_default_font_size &&
397             p_sys->i_display_height == (int)p_filter->fmt_out.video.i_height )
398             return VLC_SUCCESS;
399
400         if( p_sys->i_default_font_size )
401         {
402             i_size = p_sys->i_default_font_size;
403         }
404         else
405         {
406             var_Get( p_filter, "win32text-rel-fontsize", &val );
407             i_size = (int)p_filter->fmt_out.video.i_height / val.i_int;
408             p_filter->p_sys->i_display_height =
409                 p_filter->fmt_out.video.i_height;
410         }
411         if( i_size <= 0 )
412         {
413             msg_Warn( p_filter, "Invalid fontsize, using 12" );
414             i_size = 12;
415         }
416
417         msg_Dbg( p_filter, "Using fontsize: %i", i_size );
418     }
419
420     p_sys->i_font_size = i_size;
421
422     if( p_sys->hfont_bak ) SelectObject( p_sys->hcdc, p_sys->hfont_bak );
423     if( p_sys->hfont ) DeleteObject( p_sys->hfont );
424
425     i_size = i_size * (int64_t)p_sys->i_logpy / 72;
426
427     logfont.lfHeight = i_size;
428     logfont.lfWidth = 0;
429     logfont.lfEscapement = 0;
430     logfont.lfOrientation = 0;
431     logfont.lfWeight = 0;
432     logfont.lfItalic = FALSE;
433     logfont.lfUnderline = FALSE;
434     logfont.lfStrikeOut = FALSE;
435     logfont.lfCharSet = ANSI_CHARSET;
436     logfont.lfOutPrecision = OUT_DEFAULT_PRECIS;
437     logfont.lfClipPrecision = CLIP_DEFAULT_PRECIS;
438     logfont.lfQuality = ANTIALIASED_QUALITY;
439     logfont.lfPitchAndFamily = DEFAULT_PITCH;
440     memcpy( logfont.lfFaceName, _T("Arial"), sizeof(_T("Arial")) );
441
442     p_sys->hfont = CreateFontIndirect( &logfont );
443
444     p_sys->hfont_bak = SelectObject( p_sys->hcdc, p_sys->hfont );
445
446     return VLC_SUCCESS;
447 }