1 /*****************************************************************************
2 * win32text.c : Text drawing routines using the TextOut win32 API
3 *****************************************************************************
4 * Copyright (C) 2002 - 2009 the VideoLAN team
7 * Authors: Gildas Bazin <gbazin@videolan.org>
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23 *****************************************************************************/
25 /*****************************************************************************
27 *****************************************************************************/
33 #include <vlc_common.h>
34 #include <vlc_charset.h>
35 #include <vlc_plugin.h>
38 #include <vlc_block.h>
39 #include <vlc_filter.h>
43 /*****************************************************************************
45 *****************************************************************************/
46 static int Create ( vlc_object_t * );
47 static void Destroy( vlc_object_t * );
49 /* The RenderText call maps to pf_render_string, defined in vlc_filter.h */
50 static int RenderText( filter_t *, subpicture_region_t *,
51 subpicture_region_t *,
52 const vlc_fourcc_t * );
54 static int Render( filter_t *, subpicture_region_t *, uint8_t *, int, int);
55 static int SetFont( filter_t *, int );
57 /*****************************************************************************
59 *****************************************************************************/
60 #define FONT_TEXT N_("Font")
61 #define FONT_LONGTEXT N_("Filename for the font you want to use")
62 #define FONTSIZE_TEXT N_("Font size in pixels")
63 #define FONTSIZE_LONGTEXT N_("This is the default size of the fonts " \
64 "that will be rendered on the video. " \
65 "If set to something different than 0 this option will override the " \
66 "relative font size." )
67 #define OPACITY_TEXT N_("Opacity")
68 #define OPACITY_LONGTEXT N_("The opacity (inverse of transparency) of the " \
69 "text that will be rendered on the video. 0 = transparent, " \
70 "255 = totally opaque. " )
71 #define COLOR_TEXT N_("Text default color")
72 #define COLOR_LONGTEXT N_("The color of the text that will be rendered on "\
73 "the video. This must be an hexadecimal (like HTML colors). The first two "\
74 "chars are for red, then green, then blue. #000000 = black, #FF0000 = red,"\
75 " #00FF00 = green, #FFFF00 = yellow (red + green), #FFFFFF = white" )
76 #define FONTSIZER_TEXT N_("Relative font size")
77 #define FONTSIZER_LONGTEXT N_("This is the relative default size of the " \
78 "fonts that will be rendered on the video. If absolute font size is set, "\
79 "relative size will be overridden." )
81 static int const pi_sizes[] = { 20, 18, 16, 12, 6 };
82 static char *const ppsz_sizes_text[] = {
83 N_("Smaller"), N_("Small"), N_("Normal"), N_("Large"), N_("Larger") };
84 static const int pi_color_values[] = {
85 0x00000000, 0x00808080, 0x00C0C0C0, 0x00FFFFFF, 0x00800000,
86 0x00FF0000, 0x00FF00FF, 0x00FFFF00, 0x00808000, 0x00008000, 0x00008080,
87 0x0000FF00, 0x00800080, 0x00000080, 0x000000FF, 0x0000FFFF };
89 static const char *const ppsz_color_descriptions[] = {
90 N_("Black"), N_("Gray"), N_("Silver"), N_("White"), N_("Maroon"),
91 N_("Red"), N_("Fuchsia"), N_("Yellow"), N_("Olive"), N_("Green"), N_("Teal"),
92 N_("Lime"), N_("Purple"), N_("Navy"), N_("Blue"), N_("Aqua") };
95 set_shortname( N_("Text renderer"))
96 set_description( N_("Win32 font renderer") )
97 set_category( CAT_VIDEO )
98 set_subcategory( SUBCAT_VIDEO_SUBPIC )
100 add_integer( "win32text-fontsize", 0, FONTSIZE_TEXT,
101 FONTSIZE_LONGTEXT, true )
103 /* opacity valid on 0..255, with default 255 = fully opaque */
104 add_integer_with_range( "win32-opacity", 255, 0, 255,
105 OPACITY_TEXT, OPACITY_LONGTEXT, false )
107 /* hook to the color values list, with default 0x00ffffff = white */
108 add_integer( "win32text-color", 0x00FFFFFF, COLOR_TEXT,
109 COLOR_LONGTEXT, true )
110 change_integer_list( pi_color_values, ppsz_color_descriptions )
112 add_integer( "win32text-rel-fontsize", 16, FONTSIZER_TEXT,
113 FONTSIZER_LONGTEXT, false )
114 change_integer_list( pi_sizes, ppsz_sizes_text )
116 set_capability( "text renderer", 50 )
117 add_shortcut( "text" )
118 set_callbacks( Create, Destroy )
121 /*****************************************************************************
122 * filter_sys_t: win32text local data
123 *****************************************************************************/
126 uint8_t i_font_opacity;
130 int i_default_font_size;
131 int i_display_height;
139 static const uint8_t pi_gamma[16] =
140 {0x00, 0x41, 0x52, 0x63, 0x84, 0x85, 0x96, 0xa7, 0xb8, 0xc9,
141 0xca, 0xdb, 0xdc, 0xed, 0xee, 0xff};
143 /*****************************************************************************
144 * Create: creates the module
145 *****************************************************************************/
146 static int Create( vlc_object_t *p_this )
148 filter_t *p_filter = (filter_t *)p_this;
150 char *psz_fontfile = NULL;
154 /* Allocate structure */
155 p_filter->p_sys = p_sys = malloc( sizeof( filter_sys_t ) );
158 p_sys->i_font_size = 0;
159 p_sys->i_display_height = 0;
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 = VLC_CLIP( val.i_int, 0, 255 );
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 = VLC_CLIP( val.i_int, 0, 0xFFFFFF );
176 p_sys->hfont = p_sys->hfont_bak = 0;
178 p_sys->hcdc = CreateCompatibleDC( hdc );
179 p_sys->i_logpy = GetDeviceCaps( hdc, LOGPIXELSY );
180 ReleaseDC( NULL, hdc );
181 SetBkMode( p_sys->hcdc, TRANSPARENT );
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;
187 free( psz_fontfile );
188 p_filter->pf_render_text = RenderText;
189 p_filter->pf_render_html = NULL;
193 free( psz_fontfile );
198 /*****************************************************************************
199 * Destroy: destroy the module
200 *****************************************************************************/
201 static void Destroy( vlc_object_t *p_this )
203 filter_t *p_filter = (filter_t *)p_this;
204 filter_sys_t *p_sys = p_filter->p_sys;
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 );
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 )
223 bool b_outline = true;
225 /* Create a new subpicture region */
226 memset( &fmt, 0, sizeof(video_format_t) );
227 fmt.i_chroma = VLC_CODEC_YUVP;
228 fmt.i_width = fmt.i_visible_width = i_width + (b_outline ? 4 : 0);
229 fmt.i_height = fmt.i_visible_height = i_height + (b_outline ? 4 : 0);
230 fmt.i_x_offset = fmt.i_y_offset = 0;
235 fmt.p_palette = calloc( 1, sizeof(*fmt.p_palette) );
238 fmt.p_palette->i_entries = 16;
239 for( i = 0; i < fmt.p_palette->i_entries; i++ )
241 fmt.p_palette->palette[i][0] = pi_gamma[i];
242 fmt.p_palette->palette[i][1] = 128;
243 fmt.p_palette->palette[i][2] = 128;
244 fmt.p_palette->palette[i][3] = pi_gamma[i];
247 p_region->p_picture = picture_NewFromFormat( &fmt );
248 if( !p_region->p_picture )
250 free( fmt.p_palette );
255 p_dst = p_region->p_picture->Y_PIXELS;
256 i_pitch = p_region->p_picture->Y_PITCH;
260 memset( p_dst, 0, i_pitch * fmt.i_height );
261 p_dst += p_region->p_picture->Y_PITCH * 2 + 2;
264 for( i = 0; i < i_height; i++ )
266 memcpy( p_dst, p_bitmap, i_width );
267 p_bitmap += (i_width+3) & ~3;
271 /* Outlining (find something better than nearest neighbour filtering ?) */
274 uint8_t *p_top = p_dst; /* Use 1st line as a cache */
275 uint8_t left, current;
278 p_dst = p_region->p_picture->Y_PIXELS;
280 for( y = 1; y < (int)fmt.i_height - 1; y++ )
282 memcpy( p_top, p_dst, fmt.i_width );
286 for( x = 1; x < (int)fmt.i_width - 1; x++ )
289 p_dst[x] = ( 4 * (int)p_dst[x] + left + p_top[x] + p_dst[x+1] +
290 p_dst[x + i_pitch]) / 8;
294 memset( p_top, 0, fmt.i_width );
300 static int RenderText( filter_t *p_filter, subpicture_region_t *p_region_out,
301 subpicture_region_t *p_region_in,
302 const vlc_fourcc_t *p_chroma_list )
304 filter_sys_t *p_sys = p_filter->p_sys;
305 int i_font_color, i_font_alpha, i_font_size;
308 int i, i_width, i_height;
309 HBITMAP bitmap, bitmap_bak;
311 RECT rect = { 0, 0, 0, 0 };
314 if( !p_region_in || !p_region_out ) return VLC_EGENERIC;
315 if( !p_region_in->psz_text || !*p_region_in->psz_text )
318 psz_string = ToT(p_region_in->psz_text);
319 if( psz_string == NULL )
327 if( p_region_in->p_style )
329 i_font_color = VLC_CLIP( p_region_in->p_style->i_font_color, 0, 0xFFFFFF );
330 i_font_alpha = VLC_CLIP( p_region_in->p_style->i_font_alpha, 0, 255 );
331 i_font_size = VLC_CLIP( p_region_in->p_style->i_font_size, 0, 255 );
335 i_font_color = p_sys->i_font_color;
336 i_font_alpha = 255 - p_sys->i_font_opacity;
337 i_font_size = p_sys->i_default_font_size;
340 SetFont( p_filter, i_font_size );
342 SetTextColor( p_sys->hcdc, RGB( (i_font_color >> 16) & 0xff,
343 (i_font_color >> 8) & 0xff, i_font_color & 0xff) );
345 DrawText( p_sys->hcdc, psz_string, -1, &rect,
346 DT_CALCRECT | DT_CENTER | DT_NOPREFIX );
347 i_width = rect.right; i_height = rect.bottom;
349 p_bmi = malloc(sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD)*16);
350 memset( p_bmi, 0, sizeof(BITMAPINFOHEADER) );
351 p_bmi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
352 p_bmi->bmiHeader.biWidth = (i_width+3) & ~3;
353 p_bmi->bmiHeader.biHeight = - i_height;
354 p_bmi->bmiHeader.biPlanes = 1;
355 p_bmi->bmiHeader.biBitCount = 8;
356 p_bmi->bmiHeader.biCompression = BI_RGB;
357 p_bmi->bmiHeader.biClrUsed = 16;
359 for( i = 0; i < 16; i++ )
361 p_bmi->bmiColors[i].rgbBlue =
362 p_bmi->bmiColors[i].rgbGreen =
363 p_bmi->bmiColors[i].rgbRed = pi_gamma[i];
366 bitmap = CreateDIBSection( p_sys->hcdc, p_bmi, DIB_RGB_COLORS,
367 (void **)&p_bitmap, NULL, 0 );
370 msg_Err( p_filter, "could not create bitmap" );
375 bitmap_bak = SelectObject( p_sys->hcdc, bitmap );
376 FillRect( p_sys->hcdc, &rect, (HBRUSH)GetStockObject(BLACK_BRUSH) );
378 if( !DrawText( p_sys->hcdc, psz_string, -1, &rect,
379 DT_CENTER | DT_NOPREFIX ) )
381 msg_Err( p_filter, "could not draw text" );
384 p_region_out->i_x = p_region_in->i_x;
385 p_region_out->i_y = p_region_in->i_y;
386 Render( p_filter, p_region_out, p_bitmap, i_width, i_height );
388 SelectObject( p_sys->hcdc, bitmap_bak );
389 DeleteObject( bitmap );
394 static int SetFont( filter_t *p_filter, int i_size )
396 filter_sys_t *p_sys = p_filter->p_sys;
399 if( i_size && i_size == p_sys->i_font_size ) return VLC_SUCCESS;
405 if( !p_sys->i_default_font_size &&
406 p_sys->i_display_height == (int)p_filter->fmt_out.video.i_height )
409 if( p_sys->i_default_font_size )
411 i_size = p_sys->i_default_font_size;
415 var_Get( p_filter, "win32text-rel-fontsize", &val );
416 i_size = (int)p_filter->fmt_out.video.i_height / val.i_int;
417 p_filter->p_sys->i_display_height =
418 p_filter->fmt_out.video.i_height;
422 msg_Warn( p_filter, "invalid fontsize, using 12" );
426 msg_Dbg( p_filter, "using fontsize: %i", i_size );
429 p_sys->i_font_size = i_size;
431 if( p_sys->hfont_bak ) SelectObject( p_sys->hcdc, p_sys->hfont_bak );
432 if( p_sys->hfont ) DeleteObject( p_sys->hfont );
434 i_size = i_size * (int64_t)p_sys->i_logpy / 72;
436 logfont.lfHeight = i_size;
438 logfont.lfEscapement = 0;
439 logfont.lfOrientation = 0;
440 logfont.lfWeight = 0;
441 logfont.lfItalic = FALSE;
442 logfont.lfUnderline = FALSE;
443 logfont.lfStrikeOut = FALSE;
444 logfont.lfCharSet = ANSI_CHARSET;
445 logfont.lfOutPrecision = OUT_DEFAULT_PRECIS;
446 logfont.lfClipPrecision = CLIP_DEFAULT_PRECIS;
447 logfont.lfQuality = ANTIALIASED_QUALITY;
448 logfont.lfPitchAndFamily = DEFAULT_PITCH;
449 memcpy( logfont.lfFaceName, _T("Arial"), sizeof(_T("Arial")) );
451 p_sys->hfont = CreateFontIndirect( &logfont );
453 p_sys->hfont_bak = SelectObject( p_sys->hcdc, p_sys->hfont );