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 *****************************************************************************/
31 #include <vlc_block.h>
32 #include <vlc_filter.h>
36 /*****************************************************************************
38 *****************************************************************************/
39 static int Create ( vlc_object_t * );
40 static void Destroy( vlc_object_t * );
42 /* The RenderText call maps to pf_render_string, defined in vlc_filter.h */
43 static int RenderText( filter_t *, subpicture_region_t *,
44 subpicture_region_t * );
46 static int Render( filter_t *, subpicture_region_t *, uint8_t *, int, int);
47 static int SetFont( filter_t *, int );
49 /*****************************************************************************
51 *****************************************************************************/
52 #define FONT_TEXT N_("Font")
53 #define FONT_LONGTEXT N_("Filename for the font you want to use")
54 #define FONTSIZE_TEXT N_("Font size in pixels")
55 #define FONTSIZE_LONGTEXT N_("This is the default size of the fonts " \
56 "that will be rendered on the video. " \
57 "If set to something different than 0 this option will override the " \
58 "relative font size." )
59 #define OPACITY_TEXT N_("Opacity")
60 #define OPACITY_LONGTEXT N_("The opacity (inverse of transparency) of the " \
61 "text that will be rendered on the video. 0 = transparent, " \
62 "255 = totally opaque. " )
63 #define COLOR_TEXT N_("Text default color")
64 #define COLOR_LONGTEXT N_("The color of the text that will be rendered on "\
65 "the video. This must be an hexadecimal (like HTML colors). The first two "\
66 "chars are for red, then green, then blue. #000000 = black, #FF0000 = red,"\
67 " #00FF00 = green, #FFFF00 = yellow (red + green), #FFFFFF = white" )
68 #define FONTSIZER_TEXT N_("Relative font size")
69 #define FONTSIZER_LONGTEXT N_("This is the relative default size of the " \
70 "fonts that will be rendered on the video. If absolute font size is set, "\
71 "relative size will be overriden." )
73 static int pi_sizes[] = { 20, 18, 16, 12, 6 };
74 static char *ppsz_sizes_text[] = { N_("Smaller"), N_("Small"), N_("Normal"),
75 N_("Large"), N_("Larger") };
76 static int pi_color_values[] = {
77 0x00000000, 0x00808080, 0x00C0C0C0, 0x00FFFFFF, 0x00800000,
78 0x00FF0000, 0x00FF00FF, 0x00FFFF00, 0x00808000, 0x00008000, 0x00008080,
79 0x0000FF00, 0x00800080, 0x00000080, 0x000000FF, 0x0000FFFF };
81 static char *ppsz_color_descriptions[] = {
82 N_("Black"), N_("Gray"), N_("Silver"), N_("White"), N_("Maroon"),
83 N_("Red"), N_("Fuchsia"), N_("Yellow"), N_("Olive"), N_("Green"), N_("Teal"),
84 N_("Lime"), N_("Purple"), N_("Navy"), N_("Blue"), N_("Aqua") };
87 set_shortname( _("Text renderer"));
88 set_description( _("Win32 font renderer") );
89 set_category( CAT_VIDEO );
90 set_subcategory( SUBCAT_VIDEO_SUBPIC );
92 add_integer( "win32text-fontsize", 0, NULL, FONTSIZE_TEXT,
93 FONTSIZE_LONGTEXT, VLC_TRUE );
95 /* opacity valid on 0..255, with default 255 = fully opaque */
96 add_integer_with_range( "win32-opacity", 255, 0, 255, NULL,
97 OPACITY_TEXT, OPACITY_LONGTEXT, VLC_FALSE );
99 /* hook to the color values list, with default 0x00ffffff = white */
100 add_integer( "win32text-color", 0x00FFFFFF, NULL, COLOR_TEXT,
101 COLOR_LONGTEXT, VLC_TRUE );
102 change_integer_list( pi_color_values, ppsz_color_descriptions, 0 );
104 add_integer( "win32text-rel-fontsize", 16, NULL, FONTSIZER_TEXT,
105 FONTSIZER_LONGTEXT, VLC_FALSE );
106 change_integer_list( pi_sizes, ppsz_sizes_text, 0 );
108 set_capability( "text renderer", 50 );
109 add_shortcut( "text" );
110 set_callbacks( Create, Destroy );
113 /*****************************************************************************
114 * filter_sys_t: win32text local data
115 *****************************************************************************/
118 uint8_t i_font_opacity;
122 int i_default_font_size;
123 int i_display_height;
131 static uint8_t pi_gamma[16] =
132 {0x00, 0x41, 0x52, 0x63, 0x84, 0x85, 0x96, 0xa7, 0xb8, 0xc9,
133 0xca, 0xdb, 0xdc, 0xed, 0xee, 0xff};
135 /*****************************************************************************
136 * Create: creates the module
137 *****************************************************************************/
138 static int Create( vlc_object_t *p_this )
140 filter_t *p_filter = (filter_t *)p_this;
142 char *psz_fontfile = NULL;
146 /* Allocate structure */
147 p_filter->p_sys = p_sys = malloc( sizeof( filter_sys_t ) );
150 msg_Err( p_filter, "out of memory" );
153 p_sys->i_font_size = 0;
154 p_sys->i_display_height = 0;
156 var_Create( p_filter, "win32text-font",
157 VLC_VAR_STRING | VLC_VAR_DOINHERIT );
158 var_Create( p_filter, "win32text-fontsize",
159 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
160 var_Create( p_filter, "win32text-rel-fontsize",
161 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
162 var_Create( p_filter, "win32text-opacity",
163 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
164 var_Get( p_filter, "win32text-opacity", &val );
165 p_sys->i_font_opacity = __MAX( __MIN( val.i_int, 255 ), 0 );
166 var_Create( p_filter, "win32text-color",
167 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
168 var_Get( p_filter, "win32text-color", &val );
169 p_sys->i_font_color = __MAX( __MIN( val.i_int, 0xFFFFFF ), 0 );
171 p_sys->hfont = p_sys->hfont_bak = 0;
173 p_sys->hcdc = CreateCompatibleDC( hdc );
174 p_sys->i_logpy = GetDeviceCaps( hdc, LOGPIXELSY );
175 ReleaseDC( NULL, hdc );
176 SetBkMode( p_sys->hcdc, TRANSPARENT );
178 var_Get( p_filter, "win32text-fontsize", &val );
179 p_sys->i_default_font_size = val.i_int;
180 if( SetFont( p_filter, 0 ) != VLC_SUCCESS ) goto error;
182 if( psz_fontfile ) free( psz_fontfile );
183 p_filter->pf_render_text = RenderText;
184 p_filter->pf_render_html = NULL;
188 if( psz_fontfile ) free( psz_fontfile );
193 /*****************************************************************************
194 * Destroy: destroy the module
195 *****************************************************************************/
196 static void Destroy( vlc_object_t *p_this )
198 filter_t *p_filter = (filter_t *)p_this;
199 filter_sys_t *p_sys = p_filter->p_sys;
201 if( p_sys->hfont_bak ) SelectObject( p_sys->hcdc, p_sys->hfont_bak );
202 if( p_sys->hfont ) DeleteObject( p_sys->hfont );
203 DeleteDC( p_sys->hcdc );
207 /*****************************************************************************
208 * Render: place string in picture
209 *****************************************************************************
210 * This function merges the previously rendered win32text glyphs into a picture
211 *****************************************************************************/
212 static int Render( filter_t *p_filter, subpicture_region_t *p_region,
213 uint8_t *p_bitmap, int i_width, int i_height )
218 subpicture_region_t *p_region_tmp;
219 vlc_bool_t b_outline = VLC_TRUE;
221 /* Create a new subpicture region */
222 memset( &fmt, 0, sizeof(video_format_t) );
223 fmt.i_chroma = VLC_FOURCC('Y','U','V','P');
224 fmt.i_width = fmt.i_visible_width = i_width + (b_outline ? 4 : 0);
225 fmt.i_height = fmt.i_visible_height = i_height + (b_outline ? 4 : 0);
226 fmt.i_x_offset = fmt.i_y_offset = 0;
227 p_region_tmp = spu_CreateRegion( p_filter, &fmt );
230 msg_Err( p_filter, "cannot allocate SPU region" );
235 fmt.p_palette->i_entries = 16;
236 for( i = 0; i < fmt.p_palette->i_entries; i++ )
238 fmt.p_palette->palette[i][0] = pi_gamma[i];
239 fmt.p_palette->palette[i][1] = 128;
240 fmt.p_palette->palette[i][2] = 128;
241 fmt.p_palette->palette[i][3] = pi_gamma[i];
244 p_region->fmt = p_region_tmp->fmt;
245 p_region->picture = p_region_tmp->picture;
246 free( p_region_tmp );
248 p_dst = p_region->picture.Y_PIXELS;
249 i_pitch = p_region->picture.Y_PITCH;
253 memset( p_dst, 0, i_pitch * fmt.i_height );
254 p_dst += p_region->picture.Y_PITCH * 2 + 2;
257 for( i = 0; i < i_height; i++ )
259 memcpy( p_dst, p_bitmap, i_width );
260 p_bitmap += (i_width+3) & ~3;
264 /* Outlining (find something better than nearest neighbour filtering ?) */
267 uint8_t *p_top = p_dst; /* Use 1st line as a cache */
268 uint8_t left, current;
271 p_dst = p_region->picture.Y_PIXELS;
273 for( y = 1; y < (int)fmt.i_height - 1; y++ )
275 memcpy( p_top, p_dst, fmt.i_width );
279 for( x = 1; x < (int)fmt.i_width - 1; x++ )
282 p_dst[x] = ( 4 * (int)p_dst[x] + left + p_top[x] + p_dst[x+1] +
283 p_dst[x + i_pitch]) / 8;
287 memset( p_top, 0, fmt.i_width );
293 static int RenderText( filter_t *p_filter, subpicture_region_t *p_region_out,
294 subpicture_region_t *p_region_in )
296 filter_sys_t *p_sys = p_filter->p_sys;
297 int i_font_color, i_font_alpha, i_font_size;
300 int i, i_width, i_height;
301 HBITMAP bitmap, bitmap_bak;
307 if( !p_region_in || !p_region_out ) return VLC_EGENERIC;
309 psz_string = malloc( (strlen( p_region_in->psz_text )+1) * sizeof(TCHAR) );
310 if( mbstowcs( psz_string, p_region_in->psz_text,
311 strlen( p_region_in->psz_text ) * sizeof(TCHAR) ) < 0 )
317 psz_string = strdup( p_region_in->psz_text );
319 if( !psz_string || !*psz_string ) return VLC_EGENERIC;
321 if( p_region_in->p_style )
323 i_font_color = __MAX( __MIN( p_region_in->p_style->i_font_color, 0xFFFFFF ), 0 );
324 i_font_alpha = __MAX( __MIN( p_region_in->p_style->i_font_alpha, 255 ), 0 );
325 i_font_size = __MAX( __MIN( p_region_in->p_style->i_font_size, 255 ), 0 );
329 i_font_color = p_sys->i_font_color;
330 i_font_alpha = 255 - p_sys->i_font_opacity;
331 i_font_size = p_sys->i_default_font_size;
334 SetFont( p_filter, i_font_size );
336 SetTextColor( p_sys->hcdc, RGB( (i_font_color >> 16) & 0xff,
337 (i_font_color >> 8) & 0xff, i_font_color & 0xff) );
339 GetTextExtentExPoint( p_sys->hcdc, psz_string, _tcslen(psz_string),
341 i_width = rect.right = size.cx; i_height = rect.bottom = size.cy;
343 p_bmi = malloc(sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD)*16);
344 memset( p_bmi, 0, sizeof(BITMAPINFOHEADER) );
345 p_bmi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
346 p_bmi->bmiHeader.biWidth = (i_width+3) & ~3;
347 p_bmi->bmiHeader.biHeight = - i_height;
348 p_bmi->bmiHeader.biPlanes = 1;
349 p_bmi->bmiHeader.biBitCount = 8;
350 p_bmi->bmiHeader.biCompression = BI_RGB;
351 p_bmi->bmiHeader.biClrUsed = 16;
353 for( i = 0; i < 16; i++ )
355 p_bmi->bmiColors[i].rgbBlue =
356 p_bmi->bmiColors[i].rgbGreen =
357 p_bmi->bmiColors[i].rgbRed = pi_gamma[i];
360 bitmap = CreateDIBSection( p_sys->hcdc, p_bmi, DIB_RGB_COLORS,
361 (void **)&p_bitmap, NULL, 0 );
364 msg_Err( p_filter, "could not create bitmap" );
368 bitmap_bak = SelectObject( p_sys->hcdc, bitmap );
369 FillRect( p_sys->hcdc, &rect, (HBRUSH)GetStockObject(BLACK_BRUSH) );
371 //TextOut( p_sys->hcdc, 0, 0, psz_string, strlen(psz_string) );
372 if( !DrawText( p_sys->hcdc, psz_string, -1, &rect, 0 ) )
374 msg_Err( p_filter, "could not draw text" );
377 p_region_out->i_x = p_region_in->i_x;
378 p_region_out->i_y = p_region_in->i_y;
379 Render( p_filter, p_region_out, p_bitmap, i_width, i_height );
381 SelectObject( p_sys->hcdc, bitmap_bak );
382 DeleteObject( bitmap );
386 static int SetFont( filter_t *p_filter, int i_size )
388 filter_sys_t *p_sys = p_filter->p_sys;
391 if( i_size && i_size == p_sys->i_font_size ) return VLC_SUCCESS;
397 if( !p_sys->i_default_font_size &&
398 p_sys->i_display_height == (int)p_filter->fmt_out.video.i_height )
401 if( p_sys->i_default_font_size )
403 i_size = p_sys->i_default_font_size;
407 var_Get( p_filter, "win32text-rel-fontsize", &val );
408 i_size = (int)p_filter->fmt_out.video.i_height / val.i_int;
409 p_filter->p_sys->i_display_height =
410 p_filter->fmt_out.video.i_height;
414 msg_Warn( p_filter, "invalid fontsize, using 12" );
418 msg_Dbg( p_filter, "using fontsize: %i", i_size );
421 p_sys->i_font_size = i_size;
423 if( p_sys->hfont_bak ) SelectObject( p_sys->hcdc, p_sys->hfont_bak );
424 if( p_sys->hfont ) DeleteObject( p_sys->hfont );
426 i_size = i_size * (int64_t)p_sys->i_logpy / 72;
428 logfont.lfHeight = i_size;
430 logfont.lfEscapement = 0;
431 logfont.lfOrientation = 0;
432 logfont.lfWeight = 0;
433 logfont.lfItalic = FALSE;
434 logfont.lfUnderline = FALSE;
435 logfont.lfStrikeOut = FALSE;
436 logfont.lfCharSet = ANSI_CHARSET;
437 logfont.lfOutPrecision = OUT_DEFAULT_PRECIS;
438 logfont.lfClipPrecision = CLIP_DEFAULT_PRECIS;
439 logfont.lfQuality = ANTIALIASED_QUALITY;
440 logfont.lfPitchAndFamily = DEFAULT_PITCH;
441 memcpy( logfont.lfFaceName, _T("Arial"), sizeof(_T("Arial")) );
443 p_sys->hfont = CreateFontIndirect( &logfont );
445 p_sys->hfont_bak = SelectObject( p_sys->hcdc, p_sys->hfont );