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, 0 );
109 add_integer( "win32text-rel-fontsize", 16, NULL, FONTSIZER_TEXT,
110 FONTSIZER_LONGTEXT, false );
111 change_integer_list( pi_sizes, ppsz_sizes_text, 0 );
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 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 subpicture_region_t *p_region_tmp;
221 bool b_outline = true;
223 /* Create a new subpicture region */
224 memset( &fmt, 0, sizeof(video_format_t) );
225 fmt.i_chroma = VLC_FOURCC('Y','U','V','P');
226 fmt.i_width = fmt.i_visible_width = i_width + (b_outline ? 4 : 0);
227 fmt.i_height = fmt.i_visible_height = i_height + (b_outline ? 4 : 0);
228 fmt.i_x_offset = fmt.i_y_offset = 0;
229 p_region_tmp = spu_CreateRegion( p_filter, &fmt );
232 msg_Err( p_filter, "cannot allocate SPU region" );
237 fmt.p_palette->i_entries = 16;
238 for( i = 0; i < fmt.p_palette->i_entries; i++ )
240 fmt.p_palette->palette[i][0] = pi_gamma[i];
241 fmt.p_palette->palette[i][1] = 128;
242 fmt.p_palette->palette[i][2] = 128;
243 fmt.p_palette->palette[i][3] = pi_gamma[i];
246 p_region->fmt = p_region_tmp->fmt;
247 p_region->picture = p_region_tmp->picture;
248 free( p_region_tmp );
250 p_dst = p_region->picture.Y_PIXELS;
251 i_pitch = p_region->picture.Y_PITCH;
255 memset( p_dst, 0, i_pitch * fmt.i_height );
256 p_dst += p_region->picture.Y_PITCH * 2 + 2;
259 for( i = 0; i < i_height; i++ )
261 memcpy( p_dst, p_bitmap, i_width );
262 p_bitmap += (i_width+3) & ~3;
266 /* Outlining (find something better than nearest neighbour filtering ?) */
269 uint8_t *p_top = p_dst; /* Use 1st line as a cache */
270 uint8_t left, current;
273 p_dst = p_region->picture.Y_PIXELS;
275 for( y = 1; y < (int)fmt.i_height - 1; y++ )
277 memcpy( p_top, p_dst, fmt.i_width );
281 for( x = 1; x < (int)fmt.i_width - 1; x++ )
284 p_dst[x] = ( 4 * (int)p_dst[x] + left + p_top[x] + p_dst[x+1] +
285 p_dst[x + i_pitch]) / 8;
289 memset( p_top, 0, fmt.i_width );
295 static int RenderText( filter_t *p_filter, subpicture_region_t *p_region_out,
296 subpicture_region_t *p_region_in )
298 filter_sys_t *p_sys = p_filter->p_sys;
299 int i_font_color, i_font_alpha, i_font_size;
302 int i, i_width, i_height;
303 HBITMAP bitmap, bitmap_bak;
309 if( !p_region_in || !p_region_out ) return VLC_EGENERIC;
311 psz_string = malloc( (strlen( p_region_in->psz_text )+1) * sizeof(TCHAR) );
312 if( mbstowcs( psz_string, p_region_in->psz_text,
313 strlen( p_region_in->psz_text ) * sizeof(TCHAR) ) < 0 )
319 psz_string = strdup( p_region_in->psz_text );
321 if( !psz_string || !*psz_string ) return VLC_EGENERIC;
323 if( p_region_in->p_style )
325 i_font_color = __MAX( __MIN( p_region_in->p_style->i_font_color, 0xFFFFFF ), 0 );
326 i_font_alpha = __MAX( __MIN( p_region_in->p_style->i_font_alpha, 255 ), 0 );
327 i_font_size = __MAX( __MIN( p_region_in->p_style->i_font_size, 255 ), 0 );
331 i_font_color = p_sys->i_font_color;
332 i_font_alpha = 255 - p_sys->i_font_opacity;
333 i_font_size = p_sys->i_default_font_size;
336 SetFont( p_filter, i_font_size );
338 SetTextColor( p_sys->hcdc, RGB( (i_font_color >> 16) & 0xff,
339 (i_font_color >> 8) & 0xff, i_font_color & 0xff) );
341 GetTextExtentExPoint( p_sys->hcdc, psz_string, _tcslen(psz_string),
343 i_width = rect.right = size.cx; i_height = rect.bottom = size.cy;
345 p_bmi = malloc(sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD)*16);
346 memset( p_bmi, 0, sizeof(BITMAPINFOHEADER) );
347 p_bmi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
348 p_bmi->bmiHeader.biWidth = (i_width+3) & ~3;
349 p_bmi->bmiHeader.biHeight = - i_height;
350 p_bmi->bmiHeader.biPlanes = 1;
351 p_bmi->bmiHeader.biBitCount = 8;
352 p_bmi->bmiHeader.biCompression = BI_RGB;
353 p_bmi->bmiHeader.biClrUsed = 16;
355 for( i = 0; i < 16; i++ )
357 p_bmi->bmiColors[i].rgbBlue =
358 p_bmi->bmiColors[i].rgbGreen =
359 p_bmi->bmiColors[i].rgbRed = pi_gamma[i];
362 bitmap = CreateDIBSection( p_sys->hcdc, p_bmi, DIB_RGB_COLORS,
363 (void **)&p_bitmap, NULL, 0 );
366 msg_Err( p_filter, "could not create bitmap" );
370 bitmap_bak = SelectObject( p_sys->hcdc, bitmap );
371 FillRect( p_sys->hcdc, &rect, (HBRUSH)GetStockObject(BLACK_BRUSH) );
373 //TextOut( p_sys->hcdc, 0, 0, psz_string, strlen(psz_string) );
374 if( !DrawText( p_sys->hcdc, psz_string, -1, &rect, 0 ) )
376 msg_Err( p_filter, "could not draw text" );
379 p_region_out->i_x = p_region_in->i_x;
380 p_region_out->i_y = p_region_in->i_y;
381 Render( p_filter, p_region_out, p_bitmap, i_width, i_height );
383 SelectObject( p_sys->hcdc, bitmap_bak );
384 DeleteObject( bitmap );
388 static int SetFont( filter_t *p_filter, int i_size )
390 filter_sys_t *p_sys = p_filter->p_sys;
393 if( i_size && i_size == p_sys->i_font_size ) return VLC_SUCCESS;
399 if( !p_sys->i_default_font_size &&
400 p_sys->i_display_height == (int)p_filter->fmt_out.video.i_height )
403 if( p_sys->i_default_font_size )
405 i_size = p_sys->i_default_font_size;
409 var_Get( p_filter, "win32text-rel-fontsize", &val );
410 i_size = (int)p_filter->fmt_out.video.i_height / val.i_int;
411 p_filter->p_sys->i_display_height =
412 p_filter->fmt_out.video.i_height;
416 msg_Warn( p_filter, "invalid fontsize, using 12" );
420 msg_Dbg( p_filter, "using fontsize: %i", i_size );
423 p_sys->i_font_size = i_size;
425 if( p_sys->hfont_bak ) SelectObject( p_sys->hcdc, p_sys->hfont_bak );
426 if( p_sys->hfont ) DeleteObject( p_sys->hfont );
428 i_size = i_size * (int64_t)p_sys->i_logpy / 72;
430 logfont.lfHeight = i_size;
432 logfont.lfEscapement = 0;
433 logfont.lfOrientation = 0;
434 logfont.lfWeight = 0;
435 logfont.lfItalic = FALSE;
436 logfont.lfUnderline = FALSE;
437 logfont.lfStrikeOut = FALSE;
438 logfont.lfCharSet = ANSI_CHARSET;
439 logfont.lfOutPrecision = OUT_DEFAULT_PRECIS;
440 logfont.lfClipPrecision = CLIP_DEFAULT_PRECIS;
441 logfont.lfQuality = ANTIALIASED_QUALITY;
442 logfont.lfPitchAndFamily = DEFAULT_PITCH;
443 memcpy( logfont.lfFaceName, _T("Arial"), sizeof(_T("Arial")) );
445 p_sys->hfont = CreateFontIndirect( &logfont );
447 p_sys->hfont_bak = SelectObject( p_sys->hcdc, p_sys->hfont );