1 /*****************************************************************************
2 * win32text.c : Text drawing routines using the TextOut win32 API
3 *****************************************************************************
4 * Copyright (C) 2002 - 2005 the VideoLAN team
7 * Authors: Gildas Bazin <gbazin@videolan.org>
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.
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.
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 *****************************************************************************/
24 /*****************************************************************************
26 *****************************************************************************/
32 #include <vlc_common.h>
33 #include <vlc_plugin.h>
36 #include <vlc_block.h>
37 #include <vlc_filter.h>
41 /*****************************************************************************
43 *****************************************************************************/
44 static int Create ( vlc_object_t * );
45 static void Destroy( vlc_object_t * );
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 * );
51 static int Render( filter_t *, subpicture_region_t *, uint8_t *, int, int);
52 static int SetFont( filter_t *, int );
54 /*****************************************************************************
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." )
78 static int const pi_sizes[] = { 20, 18, 16, 12, 6 };
79 static char *const ppsz_sizes_text[] = {
80 N_("Smaller"), N_("Small"), N_("Normal"), N_("Large"), N_("Larger") };
81 static const int pi_color_values[] = {
82 0x00000000, 0x00808080, 0x00C0C0C0, 0x00FFFFFF, 0x00800000,
83 0x00FF0000, 0x00FF00FF, 0x00FFFF00, 0x00808000, 0x00008000, 0x00008080,
84 0x0000FF00, 0x00800080, 0x00000080, 0x000000FF, 0x0000FFFF };
86 static const char *const 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") };
92 set_shortname( N_("Text renderer"))
93 set_description( N_("Win32 font renderer") )
94 set_category( CAT_VIDEO )
95 set_subcategory( SUBCAT_VIDEO_SUBPIC )
97 add_integer( "win32text-fontsize", 0, NULL, FONTSIZE_TEXT,
98 FONTSIZE_LONGTEXT, true )
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 )
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, NULL );
109 add_integer( "win32text-rel-fontsize", 16, NULL, FONTSIZER_TEXT,
110 FONTSIZER_LONGTEXT, false )
111 change_integer_list( pi_sizes, ppsz_sizes_text, NULL );
113 set_capability( "text renderer", 50 )
114 add_shortcut( "text" )
115 set_callbacks( Create, Destroy )
118 /*****************************************************************************
119 * filter_sys_t: win32text local data
120 *****************************************************************************/
123 uint8_t i_font_opacity;
127 int i_default_font_size;
128 int i_display_height;
136 static const uint8_t pi_gamma[16] =
137 {0x00, 0x41, 0x52, 0x63, 0x84, 0x85, 0x96, 0xa7, 0xb8, 0xc9,
138 0xca, 0xdb, 0xdc, 0xed, 0xee, 0xff};
140 /*****************************************************************************
141 * Create: creates the module
142 *****************************************************************************/
143 static int Create( vlc_object_t *p_this )
145 filter_t *p_filter = (filter_t *)p_this;
147 char *psz_fontfile = NULL;
151 /* Allocate structure */
152 p_filter->p_sys = p_sys = malloc( sizeof( filter_sys_t ) );
155 p_sys->i_font_size = 0;
156 p_sys->i_display_height = 0;
158 var_Create( p_filter, "win32text-font",
159 VLC_VAR_STRING | VLC_VAR_DOINHERIT );
160 var_Create( p_filter, "win32text-fontsize",
161 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
162 var_Create( p_filter, "win32text-rel-fontsize",
163 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
164 var_Create( p_filter, "win32text-opacity",
165 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
166 var_Get( p_filter, "win32text-opacity", &val );
167 p_sys->i_font_opacity = __MAX( __MIN( val.i_int, 255 ), 0 );
168 var_Create( p_filter, "win32text-color",
169 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
170 var_Get( p_filter, "win32text-color", &val );
171 p_sys->i_font_color = __MAX( __MIN( val.i_int, 0xFFFFFF ), 0 );
173 p_sys->hfont = p_sys->hfont_bak = 0;
175 p_sys->hcdc = CreateCompatibleDC( hdc );
176 p_sys->i_logpy = GetDeviceCaps( hdc, LOGPIXELSY );
177 ReleaseDC( NULL, hdc );
178 SetBkMode( p_sys->hcdc, TRANSPARENT );
180 var_Get( p_filter, "win32text-fontsize", &val );
181 p_sys->i_default_font_size = val.i_int;
182 if( SetFont( p_filter, 0 ) != VLC_SUCCESS ) goto error;
184 free( psz_fontfile );
185 p_filter->pf_render_text = RenderText;
186 p_filter->pf_render_html = NULL;
190 free( psz_fontfile );
195 /*****************************************************************************
196 * Destroy: destroy the module
197 *****************************************************************************/
198 static void Destroy( vlc_object_t *p_this )
200 filter_t *p_filter = (filter_t *)p_this;
201 filter_sys_t *p_sys = p_filter->p_sys;
203 if( p_sys->hfont_bak ) SelectObject( p_sys->hcdc, p_sys->hfont_bak );
204 if( p_sys->hfont ) DeleteObject( p_sys->hfont );
205 DeleteDC( p_sys->hcdc );
209 /*****************************************************************************
210 * Render: place string in picture
211 *****************************************************************************
212 * This function merges the previously rendered win32text glyphs into a picture
213 *****************************************************************************/
214 static int Render( filter_t *p_filter, subpicture_region_t *p_region,
215 uint8_t *p_bitmap, int i_width, int i_height )
220 bool b_outline = true;
222 /* Create a new subpicture region */
223 memset( &fmt, 0, sizeof(video_format_t) );
224 fmt.i_chroma = VLC_CODEC_YUVP;
225 fmt.i_width = fmt.i_visible_width = i_width + (b_outline ? 4 : 0);
226 fmt.i_height = fmt.i_visible_height = i_height + (b_outline ? 4 : 0);
227 fmt.i_x_offset = fmt.i_y_offset = 0;
230 fmt.p_palette = calloc( 1, sizeof(*fmt.p_palette) );
233 fmt.p_palette->i_entries = 16;
234 for( i = 0; i < fmt.p_palette->i_entries; i++ )
236 fmt.p_palette->palette[i][0] = pi_gamma[i];
237 fmt.p_palette->palette[i][1] = 128;
238 fmt.p_palette->palette[i][2] = 128;
239 fmt.p_palette->palette[i][3] = pi_gamma[i];
242 p_region->p_picture = picture_New( fmt.i_chroma, fmt.i_width, fmt.i_height, fmt.i_aspect );
243 if( !p_region->p_picture )
245 free( fmt.p_palette );
250 p_dst = p_region->p_picture->Y_PIXELS;
251 i_pitch = p_region->p_picture->Y_PITCH;
255 memset( p_dst, 0, i_pitch * fmt.i_height );
256 p_dst += p_region->p_picture->Y_PITCH * 2 + 2;
259 for( i = 0; i < i_height; i++ )
261 memcpy( p_dst, p_bitmap, i_width );
262 p_bitmap += (i_width+3) & ~3;
266 /* Outlining (find something better than nearest neighbour filtering ?) */
269 uint8_t *p_top = p_dst; /* Use 1st line as a cache */
270 uint8_t left, current;
273 p_dst = p_region->p_picture->Y_PIXELS;
275 for( y = 1; y < (int)fmt.i_height - 1; y++ )
277 memcpy( p_top, p_dst, fmt.i_width );
281 for( x = 1; x < (int)fmt.i_width - 1; x++ )
284 p_dst[x] = ( 4 * (int)p_dst[x] + left + p_top[x] + p_dst[x+1] +
285 p_dst[x + i_pitch]) / 8;
289 memset( p_top, 0, fmt.i_width );
295 static int RenderText( filter_t *p_filter, subpicture_region_t *p_region_out,
296 subpicture_region_t *p_region_in )
298 filter_sys_t *p_sys = p_filter->p_sys;
299 int i_font_color, i_font_alpha, i_font_size;
302 int i, i_width, i_height;
303 HBITMAP bitmap, bitmap_bak;
309 if( !p_region_in || !p_region_out ) return VLC_EGENERIC;
310 psz_string = malloc( (strlen( p_region_in->psz_text )+1) * sizeof(TCHAR) );
314 if( mbstowcs( psz_string, p_region_in->psz_text,
315 strlen( p_region_in->psz_text ) * sizeof(TCHAR) ) < 0 )
321 strcpy( psz_string, p_region_in->psz_text );
329 if( p_region_in->p_style )
331 i_font_color = __MAX( __MIN( p_region_in->p_style->i_font_color, 0xFFFFFF ), 0 );
332 i_font_alpha = __MAX( __MIN( p_region_in->p_style->i_font_alpha, 255 ), 0 );
333 i_font_size = __MAX( __MIN( p_region_in->p_style->i_font_size, 255 ), 0 );
337 i_font_color = p_sys->i_font_color;
338 i_font_alpha = 255 - p_sys->i_font_opacity;
339 i_font_size = p_sys->i_default_font_size;
342 SetFont( p_filter, i_font_size );
344 SetTextColor( p_sys->hcdc, RGB( (i_font_color >> 16) & 0xff,
345 (i_font_color >> 8) & 0xff, i_font_color & 0xff) );
347 GetTextExtentExPoint( p_sys->hcdc, psz_string, _tcslen(psz_string),
349 i_width = rect.right = size.cx; i_height = rect.bottom = size.cy;
351 p_bmi = malloc(sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD)*16);
352 memset( p_bmi, 0, sizeof(BITMAPINFOHEADER) );
353 p_bmi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
354 p_bmi->bmiHeader.biWidth = (i_width+3) & ~3;
355 p_bmi->bmiHeader.biHeight = - i_height;
356 p_bmi->bmiHeader.biPlanes = 1;
357 p_bmi->bmiHeader.biBitCount = 8;
358 p_bmi->bmiHeader.biCompression = BI_RGB;
359 p_bmi->bmiHeader.biClrUsed = 16;
361 for( i = 0; i < 16; i++ )
363 p_bmi->bmiColors[i].rgbBlue =
364 p_bmi->bmiColors[i].rgbGreen =
365 p_bmi->bmiColors[i].rgbRed = pi_gamma[i];
368 bitmap = CreateDIBSection( p_sys->hcdc, p_bmi, DIB_RGB_COLORS,
369 (void **)&p_bitmap, NULL, 0 );
372 msg_Err( p_filter, "could not create bitmap" );
376 bitmap_bak = SelectObject( p_sys->hcdc, bitmap );
377 FillRect( p_sys->hcdc, &rect, (HBRUSH)GetStockObject(BLACK_BRUSH) );
379 //TextOut( p_sys->hcdc, 0, 0, psz_string, strlen(psz_string) );
380 if( !DrawText( p_sys->hcdc, psz_string, -1, &rect, 0 ) )
382 msg_Err( p_filter, "could not draw text" );
385 p_region_out->i_x = p_region_in->i_x;
386 p_region_out->i_y = p_region_in->i_y;
387 Render( p_filter, p_region_out, p_bitmap, i_width, i_height );
389 SelectObject( p_sys->hcdc, bitmap_bak );
390 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 );