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_plugin.h>
37 #include <vlc_block.h>
38 #include <vlc_filter.h>
42 /*****************************************************************************
44 *****************************************************************************/
45 static int Create ( vlc_object_t * );
46 static void Destroy( vlc_object_t * );
48 /* The RenderText call maps to pf_render_string, defined in vlc_filter.h */
49 static int RenderText( filter_t *, subpicture_region_t *,
50 subpicture_region_t * );
52 static int Render( filter_t *, subpicture_region_t *, uint8_t *, int, int);
53 static int SetFont( filter_t *, int );
55 /*****************************************************************************
57 *****************************************************************************/
58 #define FONT_TEXT N_("Font")
59 #define FONT_LONGTEXT N_("Filename for the font you want to use")
60 #define FONTSIZE_TEXT N_("Font size in pixels")
61 #define FONTSIZE_LONGTEXT N_("This is the default size of the fonts " \
62 "that will be rendered on the video. " \
63 "If set to something different than 0 this option will override the " \
64 "relative font size." )
65 #define OPACITY_TEXT N_("Opacity")
66 #define OPACITY_LONGTEXT N_("The opacity (inverse of transparency) of the " \
67 "text that will be rendered on the video. 0 = transparent, " \
68 "255 = totally opaque. " )
69 #define COLOR_TEXT N_("Text default color")
70 #define COLOR_LONGTEXT N_("The color of the text that will be rendered on "\
71 "the video. This must be an hexadecimal (like HTML colors). The first two "\
72 "chars are for red, then green, then blue. #000000 = black, #FF0000 = red,"\
73 " #00FF00 = green, #FFFF00 = yellow (red + green), #FFFFFF = white" )
74 #define FONTSIZER_TEXT N_("Relative font size")
75 #define FONTSIZER_LONGTEXT N_("This is the relative default size of the " \
76 "fonts that will be rendered on the video. If absolute font size is set, "\
77 "relative size will be overriden." )
79 static int const pi_sizes[] = { 20, 18, 16, 12, 6 };
80 static char *const ppsz_sizes_text[] = {
81 N_("Smaller"), N_("Small"), N_("Normal"), N_("Large"), N_("Larger") };
82 static const int pi_color_values[] = {
83 0x00000000, 0x00808080, 0x00C0C0C0, 0x00FFFFFF, 0x00800000,
84 0x00FF0000, 0x00FF00FF, 0x00FFFF00, 0x00808000, 0x00008000, 0x00008080,
85 0x0000FF00, 0x00800080, 0x00000080, 0x000000FF, 0x0000FFFF };
87 static const char *const ppsz_color_descriptions[] = {
88 N_("Black"), N_("Gray"), N_("Silver"), N_("White"), N_("Maroon"),
89 N_("Red"), N_("Fuchsia"), N_("Yellow"), N_("Olive"), N_("Green"), N_("Teal"),
90 N_("Lime"), N_("Purple"), N_("Navy"), N_("Blue"), N_("Aqua") };
93 set_shortname( N_("Text renderer"))
94 set_description( N_("Win32 font renderer") )
95 set_category( CAT_VIDEO )
96 set_subcategory( SUBCAT_VIDEO_SUBPIC )
98 add_integer( "win32text-fontsize", 0, NULL, FONTSIZE_TEXT,
99 FONTSIZE_LONGTEXT, true )
101 /* opacity valid on 0..255, with default 255 = fully opaque */
102 add_integer_with_range( "win32-opacity", 255, 0, 255, NULL,
103 OPACITY_TEXT, OPACITY_LONGTEXT, false )
105 /* hook to the color values list, with default 0x00ffffff = white */
106 add_integer( "win32text-color", 0x00FFFFFF, NULL, COLOR_TEXT,
107 COLOR_LONGTEXT, true )
108 change_integer_list( pi_color_values, ppsz_color_descriptions, NULL );
110 add_integer( "win32text-rel-fontsize", 16, NULL, FONTSIZER_TEXT,
111 FONTSIZER_LONGTEXT, false )
112 change_integer_list( pi_sizes, ppsz_sizes_text, NULL );
114 set_capability( "text renderer", 50 )
115 add_shortcut( "text" )
116 set_callbacks( Create, Destroy )
119 /*****************************************************************************
120 * filter_sys_t: win32text local data
121 *****************************************************************************/
124 uint8_t i_font_opacity;
128 int i_default_font_size;
129 int i_display_height;
137 static const uint8_t pi_gamma[16] =
138 {0x00, 0x41, 0x52, 0x63, 0x84, 0x85, 0x96, 0xa7, 0xb8, 0xc9,
139 0xca, 0xdb, 0xdc, 0xed, 0xee, 0xff};
141 /*****************************************************************************
142 * Create: creates the module
143 *****************************************************************************/
144 static int Create( vlc_object_t *p_this )
146 filter_t *p_filter = (filter_t *)p_this;
148 char *psz_fontfile = NULL;
152 /* Allocate structure */
153 p_filter->p_sys = p_sys = malloc( sizeof( filter_sys_t ) );
156 p_sys->i_font_size = 0;
157 p_sys->i_display_height = 0;
159 var_Create( p_filter, "win32text-font",
160 VLC_VAR_STRING | VLC_VAR_DOINHERIT );
161 var_Create( p_filter, "win32text-fontsize",
162 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
163 var_Create( p_filter, "win32text-rel-fontsize",
164 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
165 var_Create( p_filter, "win32text-opacity",
166 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
167 var_Get( p_filter, "win32text-opacity", &val );
168 p_sys->i_font_opacity = __MAX( __MIN( val.i_int, 255 ), 0 );
169 var_Create( p_filter, "win32text-color",
170 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
171 var_Get( p_filter, "win32text-color", &val );
172 p_sys->i_font_color = __MAX( __MIN( val.i_int, 0xFFFFFF ), 0 );
174 p_sys->hfont = p_sys->hfont_bak = 0;
176 p_sys->hcdc = CreateCompatibleDC( hdc );
177 p_sys->i_logpy = GetDeviceCaps( hdc, LOGPIXELSY );
178 ReleaseDC( NULL, hdc );
179 SetBkMode( p_sys->hcdc, TRANSPARENT );
181 var_Get( p_filter, "win32text-fontsize", &val );
182 p_sys->i_default_font_size = val.i_int;
183 if( SetFont( p_filter, 0 ) != VLC_SUCCESS ) goto error;
185 free( psz_fontfile );
186 p_filter->pf_render_text = RenderText;
187 p_filter->pf_render_html = NULL;
191 free( psz_fontfile );
196 /*****************************************************************************
197 * Destroy: destroy the module
198 *****************************************************************************/
199 static void Destroy( vlc_object_t *p_this )
201 filter_t *p_filter = (filter_t *)p_this;
202 filter_sys_t *p_sys = p_filter->p_sys;
204 if( p_sys->hfont_bak ) SelectObject( p_sys->hcdc, p_sys->hfont_bak );
205 if( p_sys->hfont ) DeleteObject( p_sys->hfont );
206 DeleteDC( p_sys->hcdc );
210 /*****************************************************************************
211 * Render: place string in picture
212 *****************************************************************************
213 * This function merges the previously rendered win32text glyphs into a picture
214 *****************************************************************************/
215 static int Render( filter_t *p_filter, subpicture_region_t *p_region,
216 uint8_t *p_bitmap, int i_width, int i_height )
221 bool b_outline = true;
223 /* Create a new subpicture region */
224 memset( &fmt, 0, sizeof(video_format_t) );
225 fmt.i_chroma = VLC_CODEC_YUVP;
226 fmt.i_width = fmt.i_visible_width = i_width + (b_outline ? 4 : 0);
227 fmt.i_height = fmt.i_visible_height = i_height + (b_outline ? 4 : 0);
228 fmt.i_x_offset = fmt.i_y_offset = 0;
231 fmt.p_palette = calloc( 1, sizeof(*fmt.p_palette) );
234 fmt.p_palette->i_entries = 16;
235 for( i = 0; i < fmt.p_palette->i_entries; i++ )
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];
243 p_region->p_picture = picture_NewFromFormat( &fmt );
244 if( !p_region->p_picture )
246 free( fmt.p_palette );
251 p_dst = p_region->p_picture->Y_PIXELS;
252 i_pitch = p_region->p_picture->Y_PITCH;
256 memset( p_dst, 0, i_pitch * fmt.i_height );
257 p_dst += p_region->p_picture->Y_PITCH * 2 + 2;
260 for( i = 0; i < i_height; i++ )
262 memcpy( p_dst, p_bitmap, i_width );
263 p_bitmap += (i_width+3) & ~3;
267 /* Outlining (find something better than nearest neighbour filtering ?) */
270 uint8_t *p_top = p_dst; /* Use 1st line as a cache */
271 uint8_t left, current;
274 p_dst = p_region->p_picture->Y_PIXELS;
276 for( y = 1; y < (int)fmt.i_height - 1; y++ )
278 memcpy( p_top, p_dst, fmt.i_width );
282 for( x = 1; x < (int)fmt.i_width - 1; x++ )
285 p_dst[x] = ( 4 * (int)p_dst[x] + left + p_top[x] + p_dst[x+1] +
286 p_dst[x + i_pitch]) / 8;
290 memset( p_top, 0, fmt.i_width );
296 static int RenderText( filter_t *p_filter, subpicture_region_t *p_region_out,
297 subpicture_region_t *p_region_in )
299 filter_sys_t *p_sys = p_filter->p_sys;
300 int i_font_color, i_font_alpha, i_font_size;
303 int i, i_width, i_height;
304 HBITMAP bitmap, bitmap_bak;
306 RECT rect = { 0, 0, 0, 0 };
309 if( !p_region_in || !p_region_out ) return VLC_EGENERIC;
310 if( !p_region_in->psz_text || !*p_region_in->psz_text )
313 psz_string = malloc( (strlen( p_region_in->psz_text )+1) * sizeof(TCHAR) );
317 if( mbstowcs( psz_string, p_region_in->psz_text,
318 strlen( p_region_in->psz_text ) * sizeof(TCHAR) ) < 0 )
324 strcpy( psz_string, p_region_in->psz_text );
332 if( p_region_in->p_style )
334 i_font_color = __MAX( __MIN( p_region_in->p_style->i_font_color, 0xFFFFFF ), 0 );
335 i_font_alpha = __MAX( __MIN( p_region_in->p_style->i_font_alpha, 255 ), 0 );
336 i_font_size = __MAX( __MIN( p_region_in->p_style->i_font_size, 255 ), 0 );
340 i_font_color = p_sys->i_font_color;
341 i_font_alpha = 255 - p_sys->i_font_opacity;
342 i_font_size = p_sys->i_default_font_size;
345 SetFont( p_filter, i_font_size );
347 SetTextColor( p_sys->hcdc, RGB( (i_font_color >> 16) & 0xff,
348 (i_font_color >> 8) & 0xff, i_font_color & 0xff) );
350 DrawText( p_sys->hcdc, psz_string, -1, &rect,
351 DT_CALCRECT | DT_CENTER | DT_NOPREFIX );
352 i_width = rect.right; i_height = rect.bottom;
354 p_bmi = malloc(sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD)*16);
355 memset( p_bmi, 0, sizeof(BITMAPINFOHEADER) );
356 p_bmi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
357 p_bmi->bmiHeader.biWidth = (i_width+3) & ~3;
358 p_bmi->bmiHeader.biHeight = - i_height;
359 p_bmi->bmiHeader.biPlanes = 1;
360 p_bmi->bmiHeader.biBitCount = 8;
361 p_bmi->bmiHeader.biCompression = BI_RGB;
362 p_bmi->bmiHeader.biClrUsed = 16;
364 for( i = 0; i < 16; i++ )
366 p_bmi->bmiColors[i].rgbBlue =
367 p_bmi->bmiColors[i].rgbGreen =
368 p_bmi->bmiColors[i].rgbRed = pi_gamma[i];
371 bitmap = CreateDIBSection( p_sys->hcdc, p_bmi, DIB_RGB_COLORS,
372 (void **)&p_bitmap, NULL, 0 );
375 msg_Err( p_filter, "could not create bitmap" );
380 bitmap_bak = SelectObject( p_sys->hcdc, bitmap );
381 FillRect( p_sys->hcdc, &rect, (HBRUSH)GetStockObject(BLACK_BRUSH) );
383 if( !DrawText( p_sys->hcdc, psz_string, -1, &rect,
384 DT_CENTER | DT_NOPREFIX ) )
386 msg_Err( p_filter, "could not draw text" );
389 p_region_out->i_x = p_region_in->i_x;
390 p_region_out->i_y = p_region_in->i_y;
391 Render( p_filter, p_region_out, p_bitmap, i_width, i_height );
393 SelectObject( p_sys->hcdc, bitmap_bak );
394 DeleteObject( bitmap );
399 static int SetFont( filter_t *p_filter, int i_size )
401 filter_sys_t *p_sys = p_filter->p_sys;
404 if( i_size && i_size == p_sys->i_font_size ) return VLC_SUCCESS;
410 if( !p_sys->i_default_font_size &&
411 p_sys->i_display_height == (int)p_filter->fmt_out.video.i_height )
414 if( p_sys->i_default_font_size )
416 i_size = p_sys->i_default_font_size;
420 var_Get( p_filter, "win32text-rel-fontsize", &val );
421 i_size = (int)p_filter->fmt_out.video.i_height / val.i_int;
422 p_filter->p_sys->i_display_height =
423 p_filter->fmt_out.video.i_height;
427 msg_Warn( p_filter, "invalid fontsize, using 12" );
431 msg_Dbg( p_filter, "using fontsize: %i", i_size );
434 p_sys->i_font_size = i_size;
436 if( p_sys->hfont_bak ) SelectObject( p_sys->hcdc, p_sys->hfont_bak );
437 if( p_sys->hfont ) DeleteObject( p_sys->hfont );
439 i_size = i_size * (int64_t)p_sys->i_logpy / 72;
441 logfont.lfHeight = i_size;
443 logfont.lfEscapement = 0;
444 logfont.lfOrientation = 0;
445 logfont.lfWeight = 0;
446 logfont.lfItalic = FALSE;
447 logfont.lfUnderline = FALSE;
448 logfont.lfStrikeOut = FALSE;
449 logfont.lfCharSet = ANSI_CHARSET;
450 logfont.lfOutPrecision = OUT_DEFAULT_PRECIS;
451 logfont.lfClipPrecision = CLIP_DEFAULT_PRECIS;
452 logfont.lfQuality = ANTIALIASED_QUALITY;
453 logfont.lfPitchAndFamily = DEFAULT_PITCH;
454 memcpy( logfont.lfFaceName, _T("Arial"), sizeof(_T("Arial")) );
456 p_sys->hfont = CreateFontIndirect( &logfont );
458 p_sys->hfont_bak = SelectObject( p_sys->hcdc, p_sys->hfont );