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 *****************************************************************************/
35 #include <vlc_block.h>
36 #include <vlc_filter.h>
40 /*****************************************************************************
42 *****************************************************************************/
43 static int Create ( vlc_object_t * );
44 static void Destroy( vlc_object_t * );
46 /* The RenderText call maps to pf_render_string, defined in vlc_filter.h */
47 static int RenderText( filter_t *, subpicture_region_t *,
48 subpicture_region_t * );
50 static int Render( filter_t *, subpicture_region_t *, uint8_t *, int, int);
51 static int SetFont( filter_t *, int );
53 /*****************************************************************************
55 *****************************************************************************/
56 #define FONT_TEXT N_("Font")
57 #define FONT_LONGTEXT N_("Filename for the font you want to use")
58 #define FONTSIZE_TEXT N_("Font size in pixels")
59 #define FONTSIZE_LONGTEXT N_("This is the default size of the fonts " \
60 "that will be rendered on the video. " \
61 "If set to something different than 0 this option will override the " \
62 "relative font size." )
63 #define OPACITY_TEXT N_("Opacity")
64 #define OPACITY_LONGTEXT N_("The opacity (inverse of transparency) of the " \
65 "text that will be rendered on the video. 0 = transparent, " \
66 "255 = totally opaque. " )
67 #define COLOR_TEXT N_("Text default color")
68 #define COLOR_LONGTEXT N_("The color of the text that will be rendered on "\
69 "the video. This must be an hexadecimal (like HTML colors). The first two "\
70 "chars are for red, then green, then blue. #000000 = black, #FF0000 = red,"\
71 " #00FF00 = green, #FFFF00 = yellow (red + green), #FFFFFF = white" )
72 #define FONTSIZER_TEXT N_("Relative font size")
73 #define FONTSIZER_LONGTEXT N_("This is the relative default size of the " \
74 "fonts that will be rendered on the video. If absolute font size is set, "\
75 "relative size will be overriden." )
77 static int pi_sizes[] = { 20, 18, 16, 12, 6 };
78 static char *ppsz_sizes_text[] = { N_("Smaller"), N_("Small"), N_("Normal"),
79 N_("Large"), N_("Larger") };
80 static int pi_color_values[] = {
81 0x00000000, 0x00808080, 0x00C0C0C0, 0x00FFFFFF, 0x00800000,
82 0x00FF0000, 0x00FF00FF, 0x00FFFF00, 0x00808000, 0x00008000, 0x00008080,
83 0x0000FF00, 0x00800080, 0x00000080, 0x000000FF, 0x0000FFFF };
85 static char *ppsz_color_descriptions[] = {
86 N_("Black"), N_("Gray"), N_("Silver"), N_("White"), N_("Maroon"),
87 N_("Red"), N_("Fuchsia"), N_("Yellow"), N_("Olive"), N_("Green"), N_("Teal"),
88 N_("Lime"), N_("Purple"), N_("Navy"), N_("Blue"), N_("Aqua") };
91 set_shortname( _("Text renderer"));
92 set_description( _("Win32 font renderer") );
93 set_category( CAT_VIDEO );
94 set_subcategory( SUBCAT_VIDEO_SUBPIC );
96 add_integer( "win32text-fontsize", 0, NULL, FONTSIZE_TEXT,
97 FONTSIZE_LONGTEXT, VLC_TRUE );
99 /* opacity valid on 0..255, with default 255 = fully opaque */
100 add_integer_with_range( "win32-opacity", 255, 0, 255, NULL,
101 OPACITY_TEXT, OPACITY_LONGTEXT, VLC_FALSE );
103 /* hook to the color values list, with default 0x00ffffff = white */
104 add_integer( "win32text-color", 0x00FFFFFF, NULL, COLOR_TEXT,
105 COLOR_LONGTEXT, VLC_TRUE );
106 change_integer_list( pi_color_values, ppsz_color_descriptions, 0 );
108 add_integer( "win32text-rel-fontsize", 16, NULL, FONTSIZER_TEXT,
109 FONTSIZER_LONGTEXT, VLC_FALSE );
110 change_integer_list( pi_sizes, ppsz_sizes_text, 0 );
112 set_capability( "text renderer", 50 );
113 add_shortcut( "text" );
114 set_callbacks( Create, Destroy );
117 /*****************************************************************************
118 * filter_sys_t: win32text local data
119 *****************************************************************************/
122 uint8_t i_font_opacity;
126 int i_default_font_size;
127 int i_display_height;
135 static uint8_t pi_gamma[16] =
136 {0x00, 0x41, 0x52, 0x63, 0x84, 0x85, 0x96, 0xa7, 0xb8, 0xc9,
137 0xca, 0xdb, 0xdc, 0xed, 0xee, 0xff};
139 /*****************************************************************************
140 * Create: creates the module
141 *****************************************************************************/
142 static int Create( vlc_object_t *p_this )
144 filter_t *p_filter = (filter_t *)p_this;
146 char *psz_fontfile = NULL;
150 /* Allocate structure */
151 p_filter->p_sys = p_sys = malloc( sizeof( filter_sys_t ) );
154 msg_Err( p_filter, "out of memory" );
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 = __MAX( __MIN( val.i_int, 255 ), 0 );
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 = __MAX( __MIN( val.i_int, 0xFFFFFF ), 0 );
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 subpicture_region_t *p_region_tmp;
223 vlc_bool_t b_outline = VLC_TRUE;
225 /* Create a new subpicture region */
226 memset( &fmt, 0, sizeof(video_format_t) );
227 fmt.i_chroma = VLC_FOURCC('Y','U','V','P');
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;
231 p_region_tmp = spu_CreateRegion( p_filter, &fmt );
234 msg_Err( p_filter, "cannot allocate SPU region" );
239 fmt.p_palette->i_entries = 16;
240 for( i = 0; i < fmt.p_palette->i_entries; i++ )
242 fmt.p_palette->palette[i][0] = pi_gamma[i];
243 fmt.p_palette->palette[i][1] = 128;
244 fmt.p_palette->palette[i][2] = 128;
245 fmt.p_palette->palette[i][3] = pi_gamma[i];
248 p_region->fmt = p_region_tmp->fmt;
249 p_region->picture = p_region_tmp->picture;
250 free( p_region_tmp );
252 p_dst = p_region->picture.Y_PIXELS;
253 i_pitch = p_region->picture.Y_PITCH;
257 memset( p_dst, 0, i_pitch * fmt.i_height );
258 p_dst += p_region->picture.Y_PITCH * 2 + 2;
261 for( i = 0; i < i_height; i++ )
263 memcpy( p_dst, p_bitmap, i_width );
264 p_bitmap += (i_width+3) & ~3;
268 /* Outlining (find something better than nearest neighbour filtering ?) */
271 uint8_t *p_top = p_dst; /* Use 1st line as a cache */
272 uint8_t left, current;
275 p_dst = p_region->picture.Y_PIXELS;
277 for( y = 1; y < (int)fmt.i_height - 1; y++ )
279 memcpy( p_top, p_dst, fmt.i_width );
283 for( x = 1; x < (int)fmt.i_width - 1; x++ )
286 p_dst[x] = ( 4 * (int)p_dst[x] + left + p_top[x] + p_dst[x+1] +
287 p_dst[x + i_pitch]) / 8;
291 memset( p_top, 0, fmt.i_width );
297 static int RenderText( filter_t *p_filter, subpicture_region_t *p_region_out,
298 subpicture_region_t *p_region_in )
300 filter_sys_t *p_sys = p_filter->p_sys;
301 int i_font_color, i_font_alpha, i_font_size;
304 int i, i_width, i_height;
305 HBITMAP bitmap, bitmap_bak;
311 if( !p_region_in || !p_region_out ) return VLC_EGENERIC;
313 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 psz_string = strdup( p_region_in->psz_text );
323 if( !psz_string || !*psz_string ) return VLC_EGENERIC;
325 if( p_region_in->p_style )
327 i_font_color = __MAX( __MIN( p_region_in->p_style->i_font_color, 0xFFFFFF ), 0 );
328 i_font_alpha = __MAX( __MIN( p_region_in->p_style->i_font_alpha, 255 ), 0 );
329 i_font_size = __MAX( __MIN( p_region_in->p_style->i_font_size, 255 ), 0 );
333 i_font_color = p_sys->i_font_color;
334 i_font_alpha = 255 - p_sys->i_font_opacity;
335 i_font_size = p_sys->i_default_font_size;
338 SetFont( p_filter, i_font_size );
340 SetTextColor( p_sys->hcdc, RGB( (i_font_color >> 16) & 0xff,
341 (i_font_color >> 8) & 0xff, i_font_color & 0xff) );
343 GetTextExtentExPoint( p_sys->hcdc, psz_string, _tcslen(psz_string),
345 i_width = rect.right = size.cx; i_height = rect.bottom = size.cy;
347 p_bmi = malloc(sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD)*16);
348 memset( p_bmi, 0, sizeof(BITMAPINFOHEADER) );
349 p_bmi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
350 p_bmi->bmiHeader.biWidth = (i_width+3) & ~3;
351 p_bmi->bmiHeader.biHeight = - i_height;
352 p_bmi->bmiHeader.biPlanes = 1;
353 p_bmi->bmiHeader.biBitCount = 8;
354 p_bmi->bmiHeader.biCompression = BI_RGB;
355 p_bmi->bmiHeader.biClrUsed = 16;
357 for( i = 0; i < 16; i++ )
359 p_bmi->bmiColors[i].rgbBlue =
360 p_bmi->bmiColors[i].rgbGreen =
361 p_bmi->bmiColors[i].rgbRed = pi_gamma[i];
364 bitmap = CreateDIBSection( p_sys->hcdc, p_bmi, DIB_RGB_COLORS,
365 (void **)&p_bitmap, NULL, 0 );
368 msg_Err( p_filter, "could not create bitmap" );
372 bitmap_bak = SelectObject( p_sys->hcdc, bitmap );
373 FillRect( p_sys->hcdc, &rect, (HBRUSH)GetStockObject(BLACK_BRUSH) );
375 //TextOut( p_sys->hcdc, 0, 0, psz_string, strlen(psz_string) );
376 if( !DrawText( p_sys->hcdc, psz_string, -1, &rect, 0 ) )
378 msg_Err( p_filter, "could not draw text" );
381 p_region_out->i_x = p_region_in->i_x;
382 p_region_out->i_y = p_region_in->i_y;
383 Render( p_filter, p_region_out, p_bitmap, i_width, i_height );
385 SelectObject( p_sys->hcdc, bitmap_bak );
386 DeleteObject( bitmap );
390 static int SetFont( filter_t *p_filter, int i_size )
392 filter_sys_t *p_sys = p_filter->p_sys;
395 if( i_size && i_size == p_sys->i_font_size ) return VLC_SUCCESS;
401 if( !p_sys->i_default_font_size &&
402 p_sys->i_display_height == (int)p_filter->fmt_out.video.i_height )
405 if( p_sys->i_default_font_size )
407 i_size = p_sys->i_default_font_size;
411 var_Get( p_filter, "win32text-rel-fontsize", &val );
412 i_size = (int)p_filter->fmt_out.video.i_height / val.i_int;
413 p_filter->p_sys->i_display_height =
414 p_filter->fmt_out.video.i_height;
418 msg_Warn( p_filter, "invalid fontsize, using 12" );
422 msg_Dbg( p_filter, "using fontsize: %i", i_size );
425 p_sys->i_font_size = i_size;
427 if( p_sys->hfont_bak ) SelectObject( p_sys->hcdc, p_sys->hfont_bak );
428 if( p_sys->hfont ) DeleteObject( p_sys->hfont );
430 i_size = i_size * (int64_t)p_sys->i_logpy / 72;
432 logfont.lfHeight = i_size;
434 logfont.lfEscapement = 0;
435 logfont.lfOrientation = 0;
436 logfont.lfWeight = 0;
437 logfont.lfItalic = FALSE;
438 logfont.lfUnderline = FALSE;
439 logfont.lfStrikeOut = FALSE;
440 logfont.lfCharSet = ANSI_CHARSET;
441 logfont.lfOutPrecision = OUT_DEFAULT_PRECIS;
442 logfont.lfClipPrecision = CLIP_DEFAULT_PRECIS;
443 logfont.lfQuality = ANTIALIASED_QUALITY;
444 logfont.lfPitchAndFamily = DEFAULT_PITCH;
445 memcpy( logfont.lfFaceName, _T("Arial"), sizeof(_T("Arial")) );
447 p_sys->hfont = CreateFontIndirect( &logfont );
449 p_sys->hfont_bak = SelectObject( p_sys->hcdc, p_sys->hfont );