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_FOURCC('Y','U','V','P');
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->i_entries = 16;
231 for( i = 0; i < fmt.p_palette->i_entries; i++ )
233 fmt.p_palette->palette[i][0] = pi_gamma[i];
234 fmt.p_palette->palette[i][1] = 128;
235 fmt.p_palette->palette[i][2] = 128;
236 fmt.p_palette->palette[i][3] = pi_gamma[i];
239 p_region->p_picture = picture_New( fmt.i_chroma, fmt.i_width, fmt.i_height, fmt.i_aspect );
240 if( !p_region->p_picture )
244 p_dst = p_region->p_picture->Y_PIXELS;
245 i_pitch = p_region->p_picture->Y_PITCH;
249 memset( p_dst, 0, i_pitch * fmt.i_height );
250 p_dst += p_region->p_picture->Y_PITCH * 2 + 2;
253 for( i = 0; i < i_height; i++ )
255 memcpy( p_dst, p_bitmap, i_width );
256 p_bitmap += (i_width+3) & ~3;
260 /* Outlining (find something better than nearest neighbour filtering ?) */
263 uint8_t *p_top = p_dst; /* Use 1st line as a cache */
264 uint8_t left, current;
267 p_dst = p_region->p_picture->Y_PIXELS;
269 for( y = 1; y < (int)fmt.i_height - 1; y++ )
271 memcpy( p_top, p_dst, fmt.i_width );
275 for( x = 1; x < (int)fmt.i_width - 1; x++ )
278 p_dst[x] = ( 4 * (int)p_dst[x] + left + p_top[x] + p_dst[x+1] +
279 p_dst[x + i_pitch]) / 8;
283 memset( p_top, 0, fmt.i_width );
289 static int RenderText( filter_t *p_filter, subpicture_region_t *p_region_out,
290 subpicture_region_t *p_region_in )
292 filter_sys_t *p_sys = p_filter->p_sys;
293 int i_font_color, i_font_alpha, i_font_size;
296 int i, i_width, i_height;
297 HBITMAP bitmap, bitmap_bak;
303 if( !p_region_in || !p_region_out ) return VLC_EGENERIC;
305 psz_string = malloc( (strlen( p_region_in->psz_text )+1) * sizeof(TCHAR) );
306 if( mbstowcs( psz_string, p_region_in->psz_text,
307 strlen( p_region_in->psz_text ) * sizeof(TCHAR) ) < 0 )
313 psz_string = strdup( p_region_in->psz_text );
315 if( !psz_string || !*psz_string ) return VLC_EGENERIC;
317 if( p_region_in->p_style )
319 i_font_color = __MAX( __MIN( p_region_in->p_style->i_font_color, 0xFFFFFF ), 0 );
320 i_font_alpha = __MAX( __MIN( p_region_in->p_style->i_font_alpha, 255 ), 0 );
321 i_font_size = __MAX( __MIN( p_region_in->p_style->i_font_size, 255 ), 0 );
325 i_font_color = p_sys->i_font_color;
326 i_font_alpha = 255 - p_sys->i_font_opacity;
327 i_font_size = p_sys->i_default_font_size;
330 SetFont( p_filter, i_font_size );
332 SetTextColor( p_sys->hcdc, RGB( (i_font_color >> 16) & 0xff,
333 (i_font_color >> 8) & 0xff, i_font_color & 0xff) );
335 GetTextExtentExPoint( p_sys->hcdc, psz_string, _tcslen(psz_string),
337 i_width = rect.right = size.cx; i_height = rect.bottom = size.cy;
339 p_bmi = malloc(sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD)*16);
340 memset( p_bmi, 0, sizeof(BITMAPINFOHEADER) );
341 p_bmi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
342 p_bmi->bmiHeader.biWidth = (i_width+3) & ~3;
343 p_bmi->bmiHeader.biHeight = - i_height;
344 p_bmi->bmiHeader.biPlanes = 1;
345 p_bmi->bmiHeader.biBitCount = 8;
346 p_bmi->bmiHeader.biCompression = BI_RGB;
347 p_bmi->bmiHeader.biClrUsed = 16;
349 for( i = 0; i < 16; i++ )
351 p_bmi->bmiColors[i].rgbBlue =
352 p_bmi->bmiColors[i].rgbGreen =
353 p_bmi->bmiColors[i].rgbRed = pi_gamma[i];
356 bitmap = CreateDIBSection( p_sys->hcdc, p_bmi, DIB_RGB_COLORS,
357 (void **)&p_bitmap, NULL, 0 );
360 msg_Err( p_filter, "could not create bitmap" );
364 bitmap_bak = SelectObject( p_sys->hcdc, bitmap );
365 FillRect( p_sys->hcdc, &rect, (HBRUSH)GetStockObject(BLACK_BRUSH) );
367 //TextOut( p_sys->hcdc, 0, 0, psz_string, strlen(psz_string) );
368 if( !DrawText( p_sys->hcdc, psz_string, -1, &rect, 0 ) )
370 msg_Err( p_filter, "could not draw text" );
373 p_region_out->i_x = p_region_in->i_x;
374 p_region_out->i_y = p_region_in->i_y;
375 Render( p_filter, p_region_out, p_bitmap, i_width, i_height );
377 SelectObject( p_sys->hcdc, bitmap_bak );
378 DeleteObject( bitmap );
382 static int SetFont( filter_t *p_filter, int i_size )
384 filter_sys_t *p_sys = p_filter->p_sys;
387 if( i_size && i_size == p_sys->i_font_size ) return VLC_SUCCESS;
393 if( !p_sys->i_default_font_size &&
394 p_sys->i_display_height == (int)p_filter->fmt_out.video.i_height )
397 if( p_sys->i_default_font_size )
399 i_size = p_sys->i_default_font_size;
403 var_Get( p_filter, "win32text-rel-fontsize", &val );
404 i_size = (int)p_filter->fmt_out.video.i_height / val.i_int;
405 p_filter->p_sys->i_display_height =
406 p_filter->fmt_out.video.i_height;
410 msg_Warn( p_filter, "invalid fontsize, using 12" );
414 msg_Dbg( p_filter, "using fontsize: %i", i_size );
417 p_sys->i_font_size = i_size;
419 if( p_sys->hfont_bak ) SelectObject( p_sys->hcdc, p_sys->hfont_bak );
420 if( p_sys->hfont ) DeleteObject( p_sys->hfont );
422 i_size = i_size * (int64_t)p_sys->i_logpy / 72;
424 logfont.lfHeight = i_size;
426 logfont.lfEscapement = 0;
427 logfont.lfOrientation = 0;
428 logfont.lfWeight = 0;
429 logfont.lfItalic = FALSE;
430 logfont.lfUnderline = FALSE;
431 logfont.lfStrikeOut = FALSE;
432 logfont.lfCharSet = ANSI_CHARSET;
433 logfont.lfOutPrecision = OUT_DEFAULT_PRECIS;
434 logfont.lfClipPrecision = CLIP_DEFAULT_PRECIS;
435 logfont.lfQuality = ANTIALIASED_QUALITY;
436 logfont.lfPitchAndFamily = DEFAULT_PITCH;
437 memcpy( logfont.lfFaceName, _T("Arial"), sizeof(_T("Arial")) );
439 p_sys->hfont = CreateFontIndirect( &logfont );
441 p_sys->hfont_bak = SelectObject( p_sys->hcdc, p_sys->hfont );