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;
190 if( psz_fontfile ) 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 vlc_bool_t b_outline = VLC_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 );