1 /*****************************************************************************
2 * win32text.c : Text drawing routines using the TextOut win32 API
3 *****************************************************************************
4 * Copyright (C) 2002 - 2009 VLC authors and VideoLAN
7 * Authors: Gildas Bazin <gbazin@videolan.org>
10 * This program is free software; you can redistribute it and/or modify it
11 * under the terms of the GNU Lesser General Public License as published by
12 * the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
20 * You should have received a copy of the GNU Lesser General Public License
21 * along with this program; if not, write to the Free Software Foundation,
22 * 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>
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 *,
51 const vlc_fourcc_t * );
53 static int Render( filter_t *, subpicture_region_t *, uint8_t *, int, int);
54 static int SetFont( filter_t *, int );
56 /*****************************************************************************
58 *****************************************************************************/
59 #define FONT_TEXT N_("Font")
60 #define FONT_LONGTEXT N_("Filename for the font you want to use")
61 #define FONTSIZE_TEXT N_("Font size in pixels")
62 #define FONTSIZE_LONGTEXT N_("This is the default size of the fonts " \
63 "that will be rendered on the video. " \
64 "If set to something different than 0 this option will override the " \
65 "relative font size." )
66 #define OPACITY_TEXT N_("Opacity")
67 #define OPACITY_LONGTEXT N_("The opacity (inverse of transparency) of the " \
68 "text that will be rendered on the video. 0 = transparent, " \
69 "255 = totally opaque. " )
70 #define COLOR_TEXT N_("Text default color")
71 #define COLOR_LONGTEXT N_("The color of the text that will be rendered on "\
72 "the video. This must be an hexadecimal (like HTML colors). The first two "\
73 "chars are for red, then green, then blue. #000000 = black, #FF0000 = red,"\
74 " #00FF00 = green, #FFFF00 = yellow (red + green), #FFFFFF = white" )
75 #define FONTSIZER_TEXT N_("Relative font size")
76 #define FONTSIZER_LONGTEXT N_("This is the relative default size of the " \
77 "fonts that will be rendered on the video. If absolute font size is set, "\
78 "relative size will be overridden." )
80 static int const pi_sizes[] = { 20, 18, 16, 12, 6 };
81 static char *const ppsz_sizes_text[] = {
82 N_("Smaller"), N_("Small"), N_("Normal"), N_("Large"), N_("Larger") };
83 static const int pi_color_values[] = {
84 0x00000000, 0x00808080, 0x00C0C0C0, 0x00FFFFFF, 0x00800000,
85 0x00FF0000, 0x00FF00FF, 0x00FFFF00, 0x00808000, 0x00008000, 0x00008080,
86 0x0000FF00, 0x00800080, 0x00000080, 0x000000FF, 0x0000FFFF };
88 static const char *const ppsz_color_descriptions[] = {
89 N_("Black"), N_("Gray"), N_("Silver"), N_("White"), N_("Maroon"),
90 N_("Red"), N_("Fuchsia"), N_("Yellow"), N_("Olive"), N_("Green"), N_("Teal"),
91 N_("Lime"), N_("Purple"), N_("Navy"), N_("Blue"), N_("Aqua") };
94 set_shortname( N_("Text renderer"))
95 set_description( N_("Win32 font renderer") )
96 set_category( CAT_VIDEO )
97 set_subcategory( SUBCAT_VIDEO_SUBPIC )
99 add_integer( "win32text-fontsize", 0, FONTSIZE_TEXT,
100 FONTSIZE_LONGTEXT, true )
102 /* opacity valid on 0..255, with default 255 = fully opaque */
103 add_integer_with_range( "win32-opacity", 255, 0, 255,
104 OPACITY_TEXT, OPACITY_LONGTEXT, false )
106 /* hook to the color values list, with default 0x00ffffff = white */
107 add_integer( "win32text-color", 0x00FFFFFF, COLOR_TEXT,
108 COLOR_LONGTEXT, true )
109 change_integer_list( pi_color_values, ppsz_color_descriptions )
111 add_integer( "win32text-rel-fontsize", 16, FONTSIZER_TEXT,
112 FONTSIZER_LONGTEXT, false )
113 change_integer_list( pi_sizes, ppsz_sizes_text )
115 set_capability( "text renderer", 50 )
116 add_shortcut( "text" )
117 set_callbacks( Create, Destroy )
120 /*****************************************************************************
121 * filter_sys_t: win32text local data
122 *****************************************************************************/
125 uint8_t i_font_opacity;
129 int i_default_font_size;
130 int i_display_height;
138 static const uint8_t pi_gamma[16] =
139 {0x00, 0x41, 0x52, 0x63, 0x84, 0x85, 0x96, 0xa7, 0xb8, 0xc9,
140 0xca, 0xdb, 0xdc, 0xed, 0xee, 0xff};
142 /*****************************************************************************
143 * Create: creates the module
144 *****************************************************************************/
145 static int Create( vlc_object_t *p_this )
147 filter_t *p_filter = (filter_t *)p_this;
149 char *psz_fontfile = NULL;
153 /* Allocate structure */
154 p_filter->p_sys = p_sys = malloc( sizeof( filter_sys_t ) );
157 p_sys->i_font_size = 0;
158 p_sys->i_display_height = 0;
160 var_Create( p_filter, "win32text-font",
161 VLC_VAR_STRING | VLC_VAR_DOINHERIT );
162 var_Create( p_filter, "win32text-fontsize",
163 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
164 var_Create( p_filter, "win32text-rel-fontsize",
165 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
166 var_Create( p_filter, "win32text-opacity",
167 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
168 var_Get( p_filter, "win32text-opacity", &val );
169 p_sys->i_font_opacity = VLC_CLIP( val.i_int, 0, 255 );
170 var_Create( p_filter, "win32text-color",
171 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
172 var_Get( p_filter, "win32text-color", &val );
173 p_sys->i_font_color = VLC_CLIP( val.i_int, 0, 0xFFFFFF );
175 p_sys->hfont = p_sys->hfont_bak = 0;
177 p_sys->hcdc = CreateCompatibleDC( hdc );
178 p_sys->i_logpy = GetDeviceCaps( hdc, LOGPIXELSY );
179 ReleaseDC( NULL, hdc );
180 SetBkMode( p_sys->hcdc, TRANSPARENT );
182 var_Get( p_filter, "win32text-fontsize", &val );
183 p_sys->i_default_font_size = val.i_int;
184 if( SetFont( p_filter, 0 ) != VLC_SUCCESS ) goto error;
186 free( psz_fontfile );
187 p_filter->pf_render_text = RenderText;
188 p_filter->pf_render_html = NULL;
192 free( psz_fontfile );
197 /*****************************************************************************
198 * Destroy: destroy the module
199 *****************************************************************************/
200 static void Destroy( vlc_object_t *p_this )
202 filter_t *p_filter = (filter_t *)p_this;
203 filter_sys_t *p_sys = p_filter->p_sys;
205 if( p_sys->hfont_bak ) SelectObject( p_sys->hcdc, p_sys->hfont_bak );
206 if( p_sys->hfont ) DeleteObject( p_sys->hfont );
207 DeleteDC( p_sys->hcdc );
211 /*****************************************************************************
212 * Render: place string in picture
213 *****************************************************************************
214 * This function merges the previously rendered win32text glyphs into a picture
215 *****************************************************************************/
216 static int Render( filter_t *p_filter, subpicture_region_t *p_region,
217 uint8_t *p_bitmap, int i_width, int i_height )
222 bool b_outline = true;
224 /* Create a new subpicture region */
225 memset( &fmt, 0, sizeof(video_format_t) );
226 fmt.i_chroma = VLC_CODEC_YUVP;
227 fmt.i_width = fmt.i_visible_width = i_width + (b_outline ? 4 : 0);
228 fmt.i_height = fmt.i_visible_height = i_height + (b_outline ? 4 : 0);
229 fmt.i_x_offset = fmt.i_y_offset = 0;
234 fmt.p_palette = calloc( 1, sizeof(*fmt.p_palette) );
237 fmt.p_palette->i_entries = 16;
238 for( i = 0; i < fmt.p_palette->i_entries; i++ )
240 fmt.p_palette->palette[i][0] = pi_gamma[i];
241 fmt.p_palette->palette[i][1] = 128;
242 fmt.p_palette->palette[i][2] = 128;
243 fmt.p_palette->palette[i][3] = pi_gamma[i];
246 p_region->p_picture = picture_NewFromFormat( &fmt );
247 if( !p_region->p_picture )
249 free( fmt.p_palette );
254 p_dst = p_region->p_picture->Y_PIXELS;
255 i_pitch = p_region->p_picture->Y_PITCH;
259 memset( p_dst, 0, i_pitch * fmt.i_height );
260 p_dst += p_region->p_picture->Y_PITCH * 2 + 2;
263 for( i = 0; i < i_height; i++ )
265 memcpy( p_dst, p_bitmap, i_width );
266 p_bitmap += (i_width+3) & ~3;
270 /* Outlining (find something better than nearest neighbour filtering ?) */
273 uint8_t *p_top = p_dst; /* Use 1st line as a cache */
274 uint8_t left, current;
277 p_dst = p_region->p_picture->Y_PIXELS;
279 for( y = 1; y < (int)fmt.i_height - 1; y++ )
281 memcpy( p_top, p_dst, fmt.i_width );
285 for( x = 1; x < (int)fmt.i_width - 1; x++ )
288 p_dst[x] = ( 4 * (int)p_dst[x] + left + p_top[x] + p_dst[x+1] +
289 p_dst[x + i_pitch]) / 8;
293 memset( p_top, 0, fmt.i_width );
299 static int RenderText( filter_t *p_filter, subpicture_region_t *p_region_out,
300 subpicture_region_t *p_region_in,
301 const vlc_fourcc_t *p_chroma_list )
303 filter_sys_t *p_sys = p_filter->p_sys;
304 int i_font_color, i_font_alpha, i_font_size;
307 int i, i_width, i_height;
308 HBITMAP bitmap, bitmap_bak;
310 RECT rect = { 0, 0, 0, 0 };
313 if( !p_region_in || !p_region_out ) return VLC_EGENERIC;
314 if( !p_region_in->psz_text || !*p_region_in->psz_text )
317 psz_string = ToT(p_region_in->psz_text);
318 if( psz_string == NULL )
326 if( p_region_in->p_style )
328 i_font_color = VLC_CLIP( p_region_in->p_style->i_font_color, 0, 0xFFFFFF );
329 i_font_alpha = VLC_CLIP( p_region_in->p_style->i_font_alpha, 0, 255 );
330 i_font_size = VLC_CLIP( p_region_in->p_style->i_font_size, 0, 255 );
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;
339 SetFont( p_filter, i_font_size );
341 SetTextColor( p_sys->hcdc, RGB( (i_font_color >> 16) & 0xff,
342 (i_font_color >> 8) & 0xff, i_font_color & 0xff) );
344 DrawText( p_sys->hcdc, psz_string, -1, &rect,
345 DT_CALCRECT | DT_CENTER | DT_NOPREFIX );
346 i_width = rect.right; i_height = rect.bottom;
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;
358 for( i = 0; i < 16; i++ )
360 p_bmi->bmiColors[i].rgbBlue =
361 p_bmi->bmiColors[i].rgbGreen =
362 p_bmi->bmiColors[i].rgbRed = pi_gamma[i];
365 bitmap = CreateDIBSection( p_sys->hcdc, p_bmi, DIB_RGB_COLORS,
366 (void **)&p_bitmap, NULL, 0 );
369 msg_Err( p_filter, "could not create bitmap" );
374 bitmap_bak = SelectObject( p_sys->hcdc, bitmap );
375 FillRect( p_sys->hcdc, &rect, (HBRUSH)GetStockObject(BLACK_BRUSH) );
377 if( !DrawText( p_sys->hcdc, psz_string, -1, &rect,
378 DT_CENTER | DT_NOPREFIX ) )
380 msg_Err( p_filter, "could not draw text" );
383 p_region_out->i_x = p_region_in->i_x;
384 p_region_out->i_y = p_region_in->i_y;
385 Render( p_filter, p_region_out, p_bitmap, i_width, i_height );
387 SelectObject( p_sys->hcdc, bitmap_bak );
388 DeleteObject( bitmap );
393 static int SetFont( filter_t *p_filter, int i_size )
395 filter_sys_t *p_sys = p_filter->p_sys;
398 if( i_size && i_size == p_sys->i_font_size ) return VLC_SUCCESS;
404 if( !p_sys->i_default_font_size &&
405 p_sys->i_display_height == (int)p_filter->fmt_out.video.i_height )
408 if( p_sys->i_default_font_size )
410 i_size = p_sys->i_default_font_size;
414 var_Get( p_filter, "win32text-rel-fontsize", &val );
415 i_size = (int)p_filter->fmt_out.video.i_height / val.i_int;
416 p_filter->p_sys->i_display_height =
417 p_filter->fmt_out.video.i_height;
421 msg_Warn( p_filter, "invalid fontsize, using 12" );
425 msg_Dbg( p_filter, "using fontsize: %i", i_size );
428 p_sys->i_font_size = i_size;
430 if( p_sys->hfont_bak ) SelectObject( p_sys->hcdc, p_sys->hfont_bak );
431 if( p_sys->hfont ) DeleteObject( p_sys->hfont );
433 i_size = i_size * (int64_t)p_sys->i_logpy / 72;
435 logfont.lfHeight = i_size;
437 logfont.lfEscapement = 0;
438 logfont.lfOrientation = 0;
439 logfont.lfWeight = 0;
440 logfont.lfItalic = FALSE;
441 logfont.lfUnderline = FALSE;
442 logfont.lfStrikeOut = FALSE;
443 logfont.lfCharSet = ANSI_CHARSET;
444 logfont.lfOutPrecision = OUT_DEFAULT_PRECIS;
445 logfont.lfClipPrecision = CLIP_DEFAULT_PRECIS;
446 logfont.lfQuality = ANTIALIASED_QUALITY;
447 logfont.lfPitchAndFamily = DEFAULT_PITCH;
448 memcpy( logfont.lfFaceName, _T("Arial"), sizeof(_T("Arial")) );
450 p_sys->hfont = CreateFontIndirect( &logfont );
452 p_sys->hfont_bak = SelectObject( p_sys->hcdc, p_sys->hfont );