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 msg_Err( p_filter
, "out of memory" );
158 p_sys
->i_font_size
= 0;
159 p_sys
->i_display_height
= 0;
161 var_Create( p_filter
, "win32text-font",
162 VLC_VAR_STRING
| VLC_VAR_DOINHERIT
);
163 var_Create( p_filter
, "win32text-fontsize",
164 VLC_VAR_INTEGER
| VLC_VAR_DOINHERIT
);
165 var_Create( p_filter
, "win32text-rel-fontsize",
166 VLC_VAR_INTEGER
| VLC_VAR_DOINHERIT
);
167 var_Create( p_filter
, "win32text-opacity",
168 VLC_VAR_INTEGER
| VLC_VAR_DOINHERIT
);
169 var_Get( p_filter
, "win32text-opacity", &val
);
170 p_sys
->i_font_opacity
= __MAX( __MIN( val
.i_int
, 255 ), 0 );
171 var_Create( p_filter
, "win32text-color",
172 VLC_VAR_INTEGER
| VLC_VAR_DOINHERIT
);
173 var_Get( p_filter
, "win32text-color", &val
);
174 p_sys
->i_font_color
= __MAX( __MIN( val
.i_int
, 0xFFFFFF ), 0 );
176 p_sys
->hfont
= p_sys
->hfont_bak
= 0;
178 p_sys
->hcdc
= CreateCompatibleDC( hdc
);
179 p_sys
->i_logpy
= GetDeviceCaps( hdc
, LOGPIXELSY
);
180 ReleaseDC( NULL
, hdc
);
181 SetBkMode( p_sys
->hcdc
, TRANSPARENT
);
183 var_Get( p_filter
, "win32text-fontsize", &val
);
184 p_sys
->i_default_font_size
= val
.i_int
;
185 if( SetFont( p_filter
, 0 ) != VLC_SUCCESS
) goto error
;
187 free( psz_fontfile
);
188 p_filter
->pf_render_text
= RenderText
;
189 p_filter
->pf_render_html
= NULL
;
193 free( psz_fontfile
);
198 /*****************************************************************************
199 * Destroy: destroy the module
200 *****************************************************************************/
201 static void Destroy( vlc_object_t
*p_this
)
203 filter_t
*p_filter
= (filter_t
*)p_this
;
204 filter_sys_t
*p_sys
= p_filter
->p_sys
;
206 if( p_sys
->hfont_bak
) SelectObject( p_sys
->hcdc
, p_sys
->hfont_bak
);
207 if( p_sys
->hfont
) DeleteObject( p_sys
->hfont
);
208 DeleteDC( p_sys
->hcdc
);
212 /*****************************************************************************
213 * Render: place string in picture
214 *****************************************************************************
215 * This function merges the previously rendered win32text glyphs into a picture
216 *****************************************************************************/
217 static int Render( filter_t
*p_filter
, subpicture_region_t
*p_region
,
218 uint8_t *p_bitmap
, int i_width
, int i_height
)
223 subpicture_region_t
*p_region_tmp
;
224 bool b_outline
= true;
226 /* Create a new subpicture region */
227 memset( &fmt
, 0, sizeof(video_format_t
) );
228 fmt
.i_chroma
= VLC_FOURCC('Y','U','V','P');
229 fmt
.i_width
= fmt
.i_visible_width
= i_width
+ (b_outline
? 4 : 0);
230 fmt
.i_height
= fmt
.i_visible_height
= i_height
+ (b_outline
? 4 : 0);
231 fmt
.i_x_offset
= fmt
.i_y_offset
= 0;
232 p_region_tmp
= spu_CreateRegion( p_filter
, &fmt
);
235 msg_Err( p_filter
, "cannot allocate SPU region" );
240 fmt
.p_palette
->i_entries
= 16;
241 for( i
= 0; i
< fmt
.p_palette
->i_entries
; i
++ )
243 fmt
.p_palette
->palette
[i
][0] = pi_gamma
[i
];
244 fmt
.p_palette
->palette
[i
][1] = 128;
245 fmt
.p_palette
->palette
[i
][2] = 128;
246 fmt
.p_palette
->palette
[i
][3] = pi_gamma
[i
];
249 p_region
->fmt
= p_region_tmp
->fmt
;
250 p_region
->picture
= p_region_tmp
->picture
;
251 free( p_region_tmp
);
253 p_dst
= p_region
->picture
.Y_PIXELS
;
254 i_pitch
= p_region
->picture
.Y_PITCH
;
258 memset( p_dst
, 0, i_pitch
* fmt
.i_height
);
259 p_dst
+= p_region
->picture
.Y_PITCH
* 2 + 2;
262 for( i
= 0; i
< i_height
; i
++ )
264 memcpy( p_dst
, p_bitmap
, i_width
);
265 p_bitmap
+= (i_width
+3) & ~3;
269 /* Outlining (find something better than nearest neighbour filtering ?) */
272 uint8_t *p_top
= p_dst
; /* Use 1st line as a cache */
273 uint8_t left
, current
;
276 p_dst
= p_region
->picture
.Y_PIXELS
;
278 for( y
= 1; y
< (int)fmt
.i_height
- 1; y
++ )
280 memcpy( p_top
, p_dst
, fmt
.i_width
);
284 for( x
= 1; x
< (int)fmt
.i_width
- 1; x
++ )
287 p_dst
[x
] = ( 4 * (int)p_dst
[x
] + left
+ p_top
[x
] + p_dst
[x
+1] +
288 p_dst
[x
+ i_pitch
]) / 8;
292 memset( p_top
, 0, fmt
.i_width
);
298 static int RenderText( filter_t
*p_filter
, subpicture_region_t
*p_region_out
,
299 subpicture_region_t
*p_region_in
)
301 filter_sys_t
*p_sys
= p_filter
->p_sys
;
302 int i_font_color
, i_font_alpha
, i_font_size
;
305 int i
, i_width
, i_height
;
306 HBITMAP bitmap
, bitmap_bak
;
312 if( !p_region_in
|| !p_region_out
) return VLC_EGENERIC
;
314 psz_string
= malloc( (strlen( p_region_in
->psz_text
)+1) * sizeof(TCHAR
) );
315 if( mbstowcs( psz_string
, p_region_in
->psz_text
,
316 strlen( p_region_in
->psz_text
) * sizeof(TCHAR
) ) < 0 )
322 psz_string
= strdup( p_region_in
->psz_text
);
324 if( !psz_string
|| !*psz_string
) return VLC_EGENERIC
;
326 if( p_region_in
->p_style
)
328 i_font_color
= __MAX( __MIN( p_region_in
->p_style
->i_font_color
, 0xFFFFFF ), 0 );
329 i_font_alpha
= __MAX( __MIN( p_region_in
->p_style
->i_font_alpha
, 255 ), 0 );
330 i_font_size
= __MAX( __MIN( p_region_in
->p_style
->i_font_size
, 255 ), 0 );
334 i_font_color
= p_sys
->i_font_color
;
335 i_font_alpha
= 255 - p_sys
->i_font_opacity
;
336 i_font_size
= p_sys
->i_default_font_size
;
339 SetFont( p_filter
, i_font_size
);
341 SetTextColor( p_sys
->hcdc
, RGB( (i_font_color
>> 16) & 0xff,
342 (i_font_color
>> 8) & 0xff, i_font_color
& 0xff) );
344 GetTextExtentExPoint( p_sys
->hcdc
, psz_string
, _tcslen(psz_string
),
346 i_width
= rect
.right
= size
.cx
; i_height
= rect
.bottom
= size
.cy
;
348 p_bmi
= malloc(sizeof(BITMAPINFOHEADER
) + sizeof(RGBQUAD
)*16);
349 memset( p_bmi
, 0, sizeof(BITMAPINFOHEADER
) );
350 p_bmi
->bmiHeader
.biSize
= sizeof(BITMAPINFOHEADER
);
351 p_bmi
->bmiHeader
.biWidth
= (i_width
+3) & ~3;
352 p_bmi
->bmiHeader
.biHeight
= - i_height
;
353 p_bmi
->bmiHeader
.biPlanes
= 1;
354 p_bmi
->bmiHeader
.biBitCount
= 8;
355 p_bmi
->bmiHeader
.biCompression
= BI_RGB
;
356 p_bmi
->bmiHeader
.biClrUsed
= 16;
358 for( i
= 0; i
< 16; i
++ )
360 p_bmi
->bmiColors
[i
].rgbBlue
=
361 p_bmi
->bmiColors
[i
].rgbGreen
=
362 p_bmi
->bmiColors
[i
].rgbRed
= pi_gamma
[i
];
365 bitmap
= CreateDIBSection( p_sys
->hcdc
, p_bmi
, DIB_RGB_COLORS
,
366 (void **)&p_bitmap
, NULL
, 0 );
369 msg_Err( p_filter
, "could not create bitmap" );
373 bitmap_bak
= SelectObject( p_sys
->hcdc
, bitmap
);
374 FillRect( p_sys
->hcdc
, &rect
, (HBRUSH
)GetStockObject(BLACK_BRUSH
) );
376 //TextOut( p_sys->hcdc, 0, 0, psz_string, strlen(psz_string) );
377 if( !DrawText( p_sys
->hcdc
, psz_string
, -1, &rect
, 0 ) )
379 msg_Err( p_filter
, "could not draw text" );
382 p_region_out
->i_x
= p_region_in
->i_x
;
383 p_region_out
->i_y
= p_region_in
->i_y
;
384 Render( p_filter
, p_region_out
, p_bitmap
, i_width
, i_height
);
386 SelectObject( p_sys
->hcdc
, bitmap_bak
);
387 DeleteObject( bitmap
);
391 static int SetFont( filter_t
*p_filter
, int i_size
)
393 filter_sys_t
*p_sys
= p_filter
->p_sys
;
396 if( i_size
&& i_size
== p_sys
->i_font_size
) return VLC_SUCCESS
;
402 if( !p_sys
->i_default_font_size
&&
403 p_sys
->i_display_height
== (int)p_filter
->fmt_out
.video
.i_height
)
406 if( p_sys
->i_default_font_size
)
408 i_size
= p_sys
->i_default_font_size
;
412 var_Get( p_filter
, "win32text-rel-fontsize", &val
);
413 i_size
= (int)p_filter
->fmt_out
.video
.i_height
/ val
.i_int
;
414 p_filter
->p_sys
->i_display_height
=
415 p_filter
->fmt_out
.video
.i_height
;
419 msg_Warn( p_filter
, "invalid fontsize, using 12" );
423 msg_Dbg( p_filter
, "using fontsize: %i", i_size
);
426 p_sys
->i_font_size
= i_size
;
428 if( p_sys
->hfont_bak
) SelectObject( p_sys
->hcdc
, p_sys
->hfont_bak
);
429 if( p_sys
->hfont
) DeleteObject( p_sys
->hfont
);
431 i_size
= i_size
* (int64_t)p_sys
->i_logpy
/ 72;
433 logfont
.lfHeight
= i_size
;
435 logfont
.lfEscapement
= 0;
436 logfont
.lfOrientation
= 0;
437 logfont
.lfWeight
= 0;
438 logfont
.lfItalic
= FALSE
;
439 logfont
.lfUnderline
= FALSE
;
440 logfont
.lfStrikeOut
= FALSE
;
441 logfont
.lfCharSet
= ANSI_CHARSET
;
442 logfont
.lfOutPrecision
= OUT_DEFAULT_PRECIS
;
443 logfont
.lfClipPrecision
= CLIP_DEFAULT_PRECIS
;
444 logfont
.lfQuality
= ANTIALIASED_QUALITY
;
445 logfont
.lfPitchAndFamily
= DEFAULT_PITCH
;
446 memcpy( logfont
.lfFaceName
, _T("Arial"), sizeof(_T("Arial")) );
448 p_sys
->hfont
= CreateFontIndirect( &logfont
);
450 p_sys
->hfont_bak
= SelectObject( p_sys
->hcdc
, p_sys
->hfont
);