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 *****************************************************************************/
27 #include <stdlib.h> /* malloc(), free() */
33 #include <vlc_block.h>
34 #include <vlc_filter.h>
38 /*****************************************************************************
40 *****************************************************************************/
41 static int Create ( vlc_object_t * );
42 static void Destroy( vlc_object_t * );
44 /* The RenderText call maps to pf_render_string, defined in vlc_filter.h */
45 static int RenderText( filter_t *, subpicture_region_t *,
46 subpicture_region_t * );
48 static int Render( filter_t *, subpicture_region_t *, uint8_t *, int, int);
49 static int SetFont( filter_t *, int );
51 /*****************************************************************************
53 *****************************************************************************/
54 #define FONT_TEXT N_("Font")
55 #define FONT_LONGTEXT N_("Filename for the font you want to use")
56 #define FONTSIZE_TEXT N_("Font size in pixels")
57 /// \bug [String] extra space
58 #define FONTSIZE_LONGTEXT N_("This is the default size of the fonts " \
59 "that will be rendered on the video. " \
60 "If set to something different than 0 this option will override the " \
61 "relative font size. " )
62 #define OPACITY_TEXT N_("Opacity")
63 #define OPACITY_LONGTEXT N_("The opacity (inverse of transparency) of the " \
64 "text that will be rendered on the video. 0 = transparent, " \
65 "255 = totally opaque. " )
66 #define COLOR_TEXT N_("Text default color")
67 #define COLOR_LONGTEXT N_("The color of the text that will be rendered on "\
68 "the video. This must be an hexadecimal (like HTML colors). The first two "\
69 "chars are for red, then green, then blue. #000000 = black, #FF0000 = red,"\
70 " #00FF00 = green, #FFFF00 = yellow (red + green), #FFFFFF = white" )
71 #define FONTSIZER_TEXT N_("Relative font size")
72 #define FONTSIZER_LONGTEXT N_("This is the relative default size of the " \
73 "fonts that will be rendered on the video. If absolute font size is set, "\
74 "relative size will be overriden." )
76 static int pi_sizes[] = { 20, 18, 16, 12, 6 };
77 static char *ppsz_sizes_text[] = { N_("Smaller"), N_("Small"), N_("Normal"),
78 N_("Large"), N_("Larger") };
79 static int pi_color_values[] = {
80 0x00000000, 0x00808080, 0x00C0C0C0, 0x00FFFFFF, 0x00800000,
81 0x00FF0000, 0x00FF00FF, 0x00FFFF00, 0x00808000, 0x00008000, 0x00008080,
82 0x0000FF00, 0x00800080, 0x00000080, 0x000000FF, 0x0000FFFF };
84 static char *ppsz_color_descriptions[] = {
85 N_("Black"), N_("Gray"), N_("Silver"), N_("White"), N_("Maroon"),
86 N_("Red"), N_("Fuchsia"), N_("Yellow"), N_("Olive"), N_("Green"), N_("Teal"),
87 N_("Lime"), N_("Purple"), N_("Navy"), N_("Blue"), N_("Aqua") };
90 set_shortname( _("Text renderer"));
91 set_description( _("Win32 font renderer") );
92 set_category( CAT_VIDEO );
93 set_subcategory( SUBCAT_VIDEO_SUBPIC );
95 add_integer( "win32text-fontsize", 0, NULL, FONTSIZE_TEXT,
96 FONTSIZE_LONGTEXT, VLC_TRUE );
98 /* opacity valid on 0..255, with default 255 = fully opaque */
99 add_integer_with_range( "win32-opacity", 255, 0, 255, NULL,
100 OPACITY_TEXT, OPACITY_LONGTEXT, VLC_FALSE );
102 /* hook to the color values list, with default 0x00ffffff = white */
103 add_integer( "win32text-color", 0x00FFFFFF, NULL, COLOR_TEXT,
104 COLOR_LONGTEXT, VLC_TRUE );
105 change_integer_list( pi_color_values, ppsz_color_descriptions, 0 );
107 add_integer( "win32text-rel-fontsize", 16, NULL, FONTSIZER_TEXT,
108 FONTSIZER_LONGTEXT, VLC_FALSE );
109 change_integer_list( pi_sizes, ppsz_sizes_text, 0 );
111 set_capability( "text renderer", 50 );
112 add_shortcut( "text" );
113 set_callbacks( Create, Destroy );
116 /*****************************************************************************
117 * filter_sys_t: win32text local data
118 *****************************************************************************/
121 uint8_t i_font_opacity;
125 int i_default_font_size;
126 int i_display_height;
134 static uint8_t pi_gamma[16] =
135 {0x00, 0x41, 0x52, 0x63, 0x84, 0x85, 0x96, 0xa7, 0xb8, 0xc9,
136 0xca, 0xdb, 0xdc, 0xed, 0xee, 0xff};
138 /*****************************************************************************
139 * Create: creates the module
140 *****************************************************************************/
141 static int Create( vlc_object_t *p_this )
143 filter_t *p_filter = (filter_t *)p_this;
145 char *psz_fontfile = NULL;
149 /* Allocate structure */
150 p_filter->p_sys = p_sys = malloc( sizeof( filter_sys_t ) );
153 msg_Err( p_filter, "out of memory" );
156 p_sys->i_font_size = 0;
157 p_sys->i_display_height = 0;
159 var_Create( p_filter, "win32text-font",
160 VLC_VAR_STRING | VLC_VAR_DOINHERIT );
161 var_Create( p_filter, "win32text-fontsize",
162 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
163 var_Create( p_filter, "win32text-rel-fontsize",
164 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
165 var_Create( p_filter, "win32text-opacity",
166 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
167 var_Get( p_filter, "win32text-opacity", &val );
168 p_sys->i_font_opacity = __MAX( __MIN( val.i_int, 255 ), 0 );
169 var_Create( p_filter, "win32text-color",
170 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
171 var_Get( p_filter, "win32text-color", &val );
172 p_sys->i_font_color = __MAX( __MIN( val.i_int, 0xFFFFFF ), 0 );
174 p_sys->hfont = p_sys->hfont_bak = 0;
176 p_sys->hcdc = CreateCompatibleDC( hdc );
177 p_sys->i_logpy = GetDeviceCaps( hdc, LOGPIXELSY );
178 ReleaseDC( NULL, hdc );
179 SetBkMode( p_sys->hcdc, TRANSPARENT );
181 var_Get( p_filter, "win32text-fontsize", &val );
182 p_sys->i_default_font_size = val.i_int;
183 if( SetFont( p_filter, 0 ) != VLC_SUCCESS ) goto error;
185 if( psz_fontfile ) free( psz_fontfile );
186 p_filter->pf_render_text = RenderText;
187 p_filter->pf_render_html = NULL;
191 if( psz_fontfile ) free( psz_fontfile );
196 /*****************************************************************************
197 * Destroy: destroy the module
198 *****************************************************************************/
199 static void Destroy( vlc_object_t *p_this )
201 filter_t *p_filter = (filter_t *)p_this;
202 filter_sys_t *p_sys = p_filter->p_sys;
204 if( p_sys->hfont_bak ) SelectObject( p_sys->hcdc, p_sys->hfont_bak );
205 if( p_sys->hfont ) DeleteObject( p_sys->hfont );
206 DeleteDC( p_sys->hcdc );
210 /*****************************************************************************
211 * Render: place string in picture
212 *****************************************************************************
213 * This function merges the previously rendered win32text glyphs into a picture
214 *****************************************************************************/
215 static int Render( filter_t *p_filter, subpicture_region_t *p_region,
216 uint8_t *p_bitmap, int i_width, int i_height )
221 subpicture_region_t *p_region_tmp;
222 vlc_bool_t b_outline = VLC_TRUE;
224 /* Create a new subpicture region */
225 memset( &fmt, 0, sizeof(video_format_t) );
226 fmt.i_chroma = VLC_FOURCC('Y','U','V','P');
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;
230 p_region_tmp = spu_CreateRegion( p_filter, &fmt );
233 msg_Err( p_filter, "cannot allocate SPU region" );
238 fmt.p_palette->i_entries = 16;
239 for( i = 0; i < fmt.p_palette->i_entries; i++ )
241 fmt.p_palette->palette[i][0] = pi_gamma[i];
242 fmt.p_palette->palette[i][1] = 128;
243 fmt.p_palette->palette[i][2] = 128;
244 fmt.p_palette->palette[i][3] = pi_gamma[i];
247 p_region->fmt = p_region_tmp->fmt;
248 p_region->picture = p_region_tmp->picture;
249 free( p_region_tmp );
251 p_dst = p_region->picture.Y_PIXELS;
252 i_pitch = p_region->picture.Y_PITCH;
256 memset( p_dst, 0, i_pitch * fmt.i_height );
257 p_dst += p_region->picture.Y_PITCH * 2 + 2;
260 for( i = 0; i < i_height; i++ )
262 memcpy( p_dst, p_bitmap, i_width );
263 p_bitmap += (i_width+3) & ~3;
267 /* Outlining (find something better than nearest neighbour filtering ?) */
270 uint8_t *p_top = p_dst; /* Use 1st line as a cache */
271 uint8_t left, current;
274 p_dst = p_region->picture.Y_PIXELS;
276 for( y = 1; y < (int)fmt.i_height - 1; y++ )
278 memcpy( p_top, p_dst, fmt.i_width );
282 for( x = 1; x < (int)fmt.i_width - 1; x++ )
285 p_dst[x] = ( 4 * (int)p_dst[x] + left + p_top[x] + p_dst[x+1] +
286 p_dst[x + i_pitch]) / 8;
290 memset( p_top, 0, fmt.i_width );
296 static int RenderText( filter_t *p_filter, subpicture_region_t *p_region_out,
297 subpicture_region_t *p_region_in )
299 filter_sys_t *p_sys = p_filter->p_sys;
300 int i_font_color, i_font_alpha, i_font_size;
303 int i, i_width, i_height;
304 HBITMAP bitmap, bitmap_bak;
310 if( !p_region_in || !p_region_out ) return VLC_EGENERIC;
312 psz_string = malloc( (strlen( p_region_in->psz_text )+1) * sizeof(TCHAR) );
313 if( mbstowcs( psz_string, p_region_in->psz_text,
314 strlen( p_region_in->psz_text ) * sizeof(TCHAR) ) < 0 )
320 psz_string = strdup( p_region_in->psz_text );
322 if( !psz_string || !*psz_string ) return VLC_EGENERIC;
324 if( p_region_in->p_style )
326 i_font_color = __MAX( __MIN( p_region_in->p_style->i_font_color, 0xFFFFFF ), 0 );
327 i_font_alpha = __MAX( __MIN( p_region_in->p_style->i_font_alpha, 255 ), 0 );
328 i_font_size = __MAX( __MIN( p_region_in->p_style->i_font_size, 255 ), 0 );
332 i_font_color = p_sys->i_font_color;
333 i_font_alpha = 255 - p_sys->i_font_opacity;
334 i_font_size = p_sys->i_default_font_size;
337 SetFont( p_filter, i_font_size );
339 SetTextColor( p_sys->hcdc, RGB( (i_font_color >> 16) & 0xff,
340 (i_font_color >> 8) & 0xff, i_font_color & 0xff) );
342 GetTextExtentExPoint( p_sys->hcdc, psz_string, _tcslen(psz_string),
344 i_width = rect.right = size.cx; i_height = rect.bottom = size.cy;
346 p_bmi = malloc(sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD)*16);
347 memset( p_bmi, 0, sizeof(BITMAPINFOHEADER) );
348 p_bmi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
349 p_bmi->bmiHeader.biWidth = (i_width+3) & ~3;
350 p_bmi->bmiHeader.biHeight = - i_height;
351 p_bmi->bmiHeader.biPlanes = 1;
352 p_bmi->bmiHeader.biBitCount = 8;
353 p_bmi->bmiHeader.biCompression = BI_RGB;
354 p_bmi->bmiHeader.biClrUsed = 16;
356 for( i = 0; i < 16; i++ )
358 p_bmi->bmiColors[i].rgbBlue =
359 p_bmi->bmiColors[i].rgbGreen =
360 p_bmi->bmiColors[i].rgbRed = pi_gamma[i];
363 bitmap = CreateDIBSection( p_sys->hcdc, p_bmi, DIB_RGB_COLORS,
364 (void **)&p_bitmap, NULL, 0 );
367 msg_Err( p_filter, "could not create bitmap" );
371 bitmap_bak = SelectObject( p_sys->hcdc, bitmap );
372 FillRect( p_sys->hcdc, &rect, (HBRUSH)GetStockObject(BLACK_BRUSH) );
374 //TextOut( p_sys->hcdc, 0, 0, psz_string, strlen(psz_string) );
375 if( !DrawText( p_sys->hcdc, psz_string, -1, &rect, 0 ) )
377 msg_Err( p_filter, "could not draw text" );
380 p_region_out->i_x = p_region_in->i_x;
381 p_region_out->i_y = p_region_in->i_y;
382 Render( p_filter, p_region_out, p_bitmap, i_width, i_height );
384 SelectObject( p_sys->hcdc, bitmap_bak );
385 DeleteObject( bitmap );
389 static int SetFont( filter_t *p_filter, int i_size )
391 filter_sys_t *p_sys = p_filter->p_sys;
394 if( i_size && i_size == p_sys->i_font_size ) return VLC_SUCCESS;
400 if( !p_sys->i_default_font_size &&
401 p_sys->i_display_height == (int)p_filter->fmt_out.video.i_height )
404 if( p_sys->i_default_font_size )
406 i_size = p_sys->i_default_font_size;
410 var_Get( p_filter, "win32text-rel-fontsize", &val );
411 i_size = (int)p_filter->fmt_out.video.i_height / val.i_int;
412 p_filter->p_sys->i_display_height =
413 p_filter->fmt_out.video.i_height;
417 msg_Warn( p_filter, "invalid fontsize, using 12" );
421 msg_Dbg( p_filter, "using fontsize: %i", i_size );
424 p_sys->i_font_size = i_size;
426 if( p_sys->hfont_bak ) SelectObject( p_sys->hcdc, p_sys->hfont_bak );
427 if( p_sys->hfont ) DeleteObject( p_sys->hfont );
429 i_size = i_size * (int64_t)p_sys->i_logpy / 72;
431 logfont.lfHeight = i_size;
433 logfont.lfEscapement = 0;
434 logfont.lfOrientation = 0;
435 logfont.lfWeight = 0;
436 logfont.lfItalic = FALSE;
437 logfont.lfUnderline = FALSE;
438 logfont.lfStrikeOut = FALSE;
439 logfont.lfCharSet = ANSI_CHARSET;
440 logfont.lfOutPrecision = OUT_DEFAULT_PRECIS;
441 logfont.lfClipPrecision = CLIP_DEFAULT_PRECIS;
442 logfont.lfQuality = ANTIALIASED_QUALITY;
443 logfont.lfPitchAndFamily = DEFAULT_PITCH;
444 memcpy( logfont.lfFaceName, _T("Arial"), sizeof(_T("Arial")) );
446 p_sys->hfont = CreateFontIndirect( &logfont );
448 p_sys->hfont_bak = SelectObject( p_sys->hcdc, p_sys->hfont );