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 #define FONTSIZE_LONGTEXT N_("This is the default size of the fonts " \
58 "that will be rendered on the video. " \
59 "If set to something different than 0 this option will override the " \
60 "relative font size. " )
61 #define OPACITY_TEXT N_("Opacity")
62 #define OPACITY_LONGTEXT N_("The opacity (inverse of transparency) of the " \
63 "text that will be rendered on the video. 0 = transparent, " \
64 "255 = totally opaque. " )
65 #define COLOR_TEXT N_("Text default color")
66 #define COLOR_LONGTEXT N_("The color of the text that will be rendered on "\
67 "the video. This must be an hexadecimal (like HTML colors). The first two "\ "chars are for red, then green, then blue. #000000 = black, #FF0000 = red,"\ " #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;
187 if( psz_fontfile ) free( psz_fontfile );
192 /*****************************************************************************
193 * Destroy: destroy the module
194 *****************************************************************************/
195 static void Destroy( vlc_object_t *p_this )
197 filter_t *p_filter = (filter_t *)p_this;
198 filter_sys_t *p_sys = p_filter->p_sys;
200 if( p_sys->hfont_bak ) SelectObject( p_sys->hcdc, p_sys->hfont_bak );
201 if( p_sys->hfont ) DeleteObject( p_sys->hfont );
202 DeleteDC( p_sys->hcdc );
206 /*****************************************************************************
207 * Render: place string in picture
208 *****************************************************************************
209 * This function merges the previously rendered win32text glyphs into a picture
210 *****************************************************************************/
211 static int Render( filter_t *p_filter, subpicture_region_t *p_region,
212 uint8_t *p_bitmap, int i_width, int i_height )
217 subpicture_region_t *p_region_tmp;
218 vlc_bool_t b_outline = VLC_TRUE;
220 /* Create a new subpicture region */
221 memset( &fmt, 0, sizeof(video_format_t) );
222 fmt.i_chroma = VLC_FOURCC('Y','U','V','P');
223 fmt.i_width = fmt.i_visible_width = i_width + (b_outline ? 4 : 0);
224 fmt.i_height = fmt.i_visible_height = i_height + (b_outline ? 4 : 0);
225 fmt.i_x_offset = fmt.i_y_offset = 0;
226 p_region_tmp = spu_CreateRegion( p_filter, &fmt );
229 msg_Err( p_filter, "cannot allocate SPU region" );
234 fmt.p_palette->i_entries = 16;
235 for( i = 0; i < fmt.p_palette->i_entries; i++ )
237 fmt.p_palette->palette[i][0] = pi_gamma[i];
238 fmt.p_palette->palette[i][1] = 128;
239 fmt.p_palette->palette[i][2] = 128;
240 fmt.p_palette->palette[i][3] = pi_gamma[i];
243 p_region->fmt = p_region_tmp->fmt;
244 p_region->picture = p_region_tmp->picture;
245 free( p_region_tmp );
247 p_dst = p_region->picture.Y_PIXELS;
248 i_pitch = p_region->picture.Y_PITCH;
252 memset( p_dst, 0, i_pitch * fmt.i_height );
253 p_dst += p_region->picture.Y_PITCH * 2 + 2;
256 for( i = 0; i < i_height; i++ )
258 memcpy( p_dst, p_bitmap, i_width );
259 p_bitmap += (i_width+3) & ~3;
263 /* Outlining (find something better than nearest neighbour filtering ?) */
266 uint8_t *p_top = p_dst; /* Use 1st line as a cache */
267 uint8_t left, current;
270 p_dst = p_region->picture.Y_PIXELS;
272 for( y = 1; y < (int)fmt.i_height - 1; y++ )
274 memcpy( p_top, p_dst, fmt.i_width );
278 for( x = 1; x < (int)fmt.i_width - 1; x++ )
281 p_dst[x] = ( 4 * (int)p_dst[x] + left + p_top[x] + p_dst[x+1] +
282 p_dst[x + i_pitch]) / 8;
286 memset( p_top, 0, fmt.i_width );
292 static int RenderText( filter_t *p_filter, subpicture_region_t *p_region_out,
293 subpicture_region_t *p_region_in )
295 filter_sys_t *p_sys = p_filter->p_sys;
296 int i_font_color, i_font_alpha, i_font_size;
299 int i, i_width, i_height;
300 HBITMAP bitmap, bitmap_bak;
306 if( !p_region_in || !p_region_out ) return VLC_EGENERIC;
308 psz_string = malloc( (strlen( p_region_in->psz_text )+1) * sizeof(TCHAR) );
309 if( mbstowcs( psz_string, p_region_in->psz_text,
310 strlen( p_region_in->psz_text ) * sizeof(TCHAR) ) < 0 )
316 psz_string = strdup( p_region_in->psz_text );
318 if( !psz_string || !*psz_string ) return VLC_EGENERIC;
320 if( p_region_in->p_style )
322 i_font_color = __MAX( __MIN( p_region_in->p_style->i_font_color, 0xFFFFFF ), 0 );
323 i_font_alpha = __MAX( __MIN( p_region_in->p_style->i_font_alpha, 255 ), 0 );
324 i_font_size = __MAX( __MIN( p_region_in->p_style->i_font_size, 255 ), 0 );
328 i_font_color = p_sys->i_font_color;
329 i_font_alpha = 255 - p_sys->i_font_opacity;
330 i_font_size = p_sys->i_default_font_size;
333 SetFont( p_filter, i_font_size );
335 SetTextColor( p_sys->hcdc, RGB( (i_font_color >> 16) & 0xff,
336 (i_font_color >> 8) & 0xff, i_font_color & 0xff) );
338 GetTextExtentExPoint( p_sys->hcdc, psz_string, _tcslen(psz_string),
340 i_width = rect.right = size.cx; i_height = rect.bottom = size.cy;
342 p_bmi = malloc(sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD)*16);
343 memset( p_bmi, 0, sizeof(BITMAPINFOHEADER) );
344 p_bmi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
345 p_bmi->bmiHeader.biWidth = (i_width+3) & ~3;
346 p_bmi->bmiHeader.biHeight = - i_height;
347 p_bmi->bmiHeader.biPlanes = 1;
348 p_bmi->bmiHeader.biBitCount = 8;
349 p_bmi->bmiHeader.biCompression = BI_RGB;
350 p_bmi->bmiHeader.biClrUsed = 16;
352 for( i = 0; i < 16; i++ )
354 p_bmi->bmiColors[i].rgbBlue =
355 p_bmi->bmiColors[i].rgbGreen =
356 p_bmi->bmiColors[i].rgbRed = pi_gamma[i];
359 bitmap = CreateDIBSection( p_sys->hcdc, p_bmi, DIB_RGB_COLORS,
360 (void **)&p_bitmap, NULL, 0 );
363 msg_Err( p_filter, "could not create bitmap" );
367 bitmap_bak = SelectObject( p_sys->hcdc, bitmap );
368 FillRect( p_sys->hcdc, &rect, (HBRUSH)GetStockObject(BLACK_BRUSH) );
370 //TextOut( p_sys->hcdc, 0, 0, psz_string, strlen(psz_string) );
371 if( !DrawText( p_sys->hcdc, psz_string, -1, &rect, 0 ) )
373 msg_Err( p_filter, "could not draw text" );
376 p_region_out->i_x = p_region_in->i_x;
377 p_region_out->i_y = p_region_in->i_y;
378 Render( p_filter, p_region_out, p_bitmap, i_width, i_height );
380 SelectObject( p_sys->hcdc, bitmap_bak );
381 DeleteObject( bitmap );
385 static int SetFont( filter_t *p_filter, int i_size )
387 filter_sys_t *p_sys = p_filter->p_sys;
390 if( i_size && i_size == p_sys->i_font_size ) return VLC_SUCCESS;
396 if( !p_sys->i_default_font_size &&
397 p_sys->i_display_height == (int)p_filter->fmt_out.video.i_height )
400 if( p_sys->i_default_font_size )
402 i_size = p_sys->i_default_font_size;
406 var_Get( p_filter, "win32text-rel-fontsize", &val );
407 i_size = (int)p_filter->fmt_out.video.i_height / val.i_int;
408 p_filter->p_sys->i_display_height =
409 p_filter->fmt_out.video.i_height;
413 msg_Warn( p_filter, "Invalid fontsize, using 12" );
417 msg_Dbg( p_filter, "Using fontsize: %i", i_size );
420 p_sys->i_font_size = i_size;
422 if( p_sys->hfont_bak ) SelectObject( p_sys->hcdc, p_sys->hfont_bak );
423 if( p_sys->hfont ) DeleteObject( p_sys->hfont );
425 i_size = i_size * (int64_t)p_sys->i_logpy / 72;
427 logfont.lfHeight = i_size;
429 logfont.lfEscapement = 0;
430 logfont.lfOrientation = 0;
431 logfont.lfWeight = 0;
432 logfont.lfItalic = FALSE;
433 logfont.lfUnderline = FALSE;
434 logfont.lfStrikeOut = FALSE;
435 logfont.lfCharSet = ANSI_CHARSET;
436 logfont.lfOutPrecision = OUT_DEFAULT_PRECIS;
437 logfont.lfClipPrecision = CLIP_DEFAULT_PRECIS;
438 logfont.lfQuality = ANTIALIASED_QUALITY;
439 logfont.lfPitchAndFamily = DEFAULT_PITCH;
440 memcpy( logfont.lfFaceName, _T("Arial"), sizeof(_T("Arial")) );
442 p_sys->hfont = CreateFontIndirect( &logfont );
444 p_sys->hfont_bak = SelectObject( p_sys->hcdc, p_sys->hfont );