]> git.sesse.net Git - vlc/blob - modules/misc/win32text.c
Another bunch of useless test.
[vlc] / modules / misc / win32text.c
1 /*****************************************************************************
2  * win32text.c : Text drawing routines using the TextOut win32 API
3  *****************************************************************************
4  * Copyright (C) 2002 - 2005 the VideoLAN team
5  * $Id$
6  *
7  * Authors: Gildas Bazin <gbazin@videolan.org>
8  *
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.
13  *
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.
18  *
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  *****************************************************************************/
23
24 /*****************************************************************************
25  * Preamble
26  *****************************************************************************/
27
28 #ifdef HAVE_CONFIG_H
29 # include "config.h"
30 #endif
31
32 #include <vlc/vlc.h>
33 #include <vlc_vout.h>
34 #include <vlc_osd.h>
35 #include <vlc_block.h>
36 #include <vlc_filter.h>
37
38 #include <math.h>
39
40 /*****************************************************************************
41  * Local prototypes
42  *****************************************************************************/
43 static int  Create ( vlc_object_t * );
44 static void Destroy( vlc_object_t * );
45
46 /* The RenderText call maps to pf_render_string, defined in vlc_filter.h */
47 static int RenderText( filter_t *, subpicture_region_t *,
48                        subpicture_region_t * );
49
50 static int Render( filter_t *, subpicture_region_t *, uint8_t *, int, int);
51 static int SetFont( filter_t *, int );
52
53 /*****************************************************************************
54  * Module descriptor
55  *****************************************************************************/
56 #define FONT_TEXT N_("Font")
57 #define FONT_LONGTEXT N_("Filename for the font you want to use")
58 #define FONTSIZE_TEXT N_("Font size in pixels")
59 #define FONTSIZE_LONGTEXT N_("This is the default size of the fonts " \
60      "that will be rendered on the video. " \
61      "If set to something different than 0 this option will override the " \
62      "relative font size." )
63 #define OPACITY_TEXT N_("Opacity")
64 #define OPACITY_LONGTEXT N_("The opacity (inverse of transparency) of the " \
65      "text that will be rendered on the video. 0 = transparent, " \
66      "255 = totally opaque. " )
67 #define COLOR_TEXT N_("Text default color")
68 #define COLOR_LONGTEXT N_("The color of the text that will be rendered on "\
69   "the video. This must be an hexadecimal (like HTML colors). The first two "\
70   "chars are for red, then green, then blue. #000000 = black, #FF0000 = red,"\
71   " #00FF00 = green, #FFFF00 = yellow (red + green), #FFFFFF = white" )
72 #define FONTSIZER_TEXT N_("Relative font size")
73 #define FONTSIZER_LONGTEXT N_("This is the relative default size of the " \
74   "fonts that will be rendered on the video. If absolute font size is set, "\
75    "relative size will be overriden." )
76
77 static int   pi_sizes[] = { 20, 18, 16, 12, 6 };
78 static char *ppsz_sizes_text[] = { N_("Smaller"), N_("Small"), N_("Normal"),
79                                    N_("Large"), N_("Larger") };
80 static int pi_color_values[] = {
81   0x00000000, 0x00808080, 0x00C0C0C0, 0x00FFFFFF, 0x00800000,
82   0x00FF0000, 0x00FF00FF, 0x00FFFF00, 0x00808000, 0x00008000, 0x00008080,
83   0x0000FF00, 0x00800080, 0x00000080, 0x000000FF, 0x0000FFFF };
84
85 static char *ppsz_color_descriptions[] = {
86   N_("Black"), N_("Gray"), N_("Silver"), N_("White"), N_("Maroon"),
87   N_("Red"), N_("Fuchsia"), N_("Yellow"), N_("Olive"), N_("Green"), N_("Teal"),
88   N_("Lime"), N_("Purple"), N_("Navy"), N_("Blue"), N_("Aqua") };
89
90 vlc_module_begin();
91     set_shortname( _("Text renderer"));
92     set_description( _("Win32 font renderer") );
93     set_category( CAT_VIDEO );
94     set_subcategory( SUBCAT_VIDEO_SUBPIC );
95
96     add_integer( "win32text-fontsize", 0, NULL, FONTSIZE_TEXT,
97                  FONTSIZE_LONGTEXT, VLC_TRUE );
98
99     /* opacity valid on 0..255, with default 255 = fully opaque */
100     add_integer_with_range( "win32-opacity", 255, 0, 255, NULL,
101         OPACITY_TEXT, OPACITY_LONGTEXT, VLC_FALSE );
102
103     /* hook to the color values list, with default 0x00ffffff = white */
104     add_integer( "win32text-color", 0x00FFFFFF, NULL, COLOR_TEXT,
105                  COLOR_LONGTEXT, VLC_TRUE );
106         change_integer_list( pi_color_values, ppsz_color_descriptions, 0 );
107
108     add_integer( "win32text-rel-fontsize", 16, NULL, FONTSIZER_TEXT,
109                  FONTSIZER_LONGTEXT, VLC_FALSE );
110         change_integer_list( pi_sizes, ppsz_sizes_text, 0 );
111
112     set_capability( "text renderer", 50 );
113     add_shortcut( "text" );
114     set_callbacks( Create, Destroy );
115 vlc_module_end();
116
117 /*****************************************************************************
118  * filter_sys_t: win32text local data
119  *****************************************************************************/
120 struct filter_sys_t
121 {
122     uint8_t        i_font_opacity;
123     int            i_font_color;
124     int            i_font_size;
125
126     int            i_default_font_size;
127     int            i_display_height;
128
129     HDC hcdc;
130     HFONT hfont;
131     HFONT hfont_bak;
132     int i_logpy;
133 };
134
135 static uint8_t pi_gamma[16] =
136   {0x00, 0x41, 0x52, 0x63, 0x84, 0x85, 0x96, 0xa7, 0xb8, 0xc9,
137    0xca, 0xdb, 0xdc, 0xed, 0xee, 0xff};
138
139 /*****************************************************************************
140  * Create: creates the module
141  *****************************************************************************/
142 static int Create( vlc_object_t *p_this )
143 {
144     filter_t *p_filter = (filter_t *)p_this;
145     filter_sys_t *p_sys;
146     char *psz_fontfile = NULL;
147     vlc_value_t val;
148     HDC hdc;
149
150     /* Allocate structure */
151     p_filter->p_sys = p_sys = malloc( sizeof( filter_sys_t ) );
152     if( !p_sys )
153     {
154         msg_Err( p_filter, "out of memory" );
155         return VLC_ENOMEM;
156     }
157     p_sys->i_font_size = 0;
158     p_sys->i_display_height = 0;
159
160     var_Create( p_filter, "win32text-font",
161                 VLC_VAR_STRING | VLC_VAR_DOINHERIT );
162     var_Create( p_filter, "win32text-fontsize",
163                 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
164     var_Create( p_filter, "win32text-rel-fontsize",
165                 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
166     var_Create( p_filter, "win32text-opacity",
167                 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
168     var_Get( p_filter, "win32text-opacity", &val );
169     p_sys->i_font_opacity = __MAX( __MIN( val.i_int, 255 ), 0 );
170     var_Create( p_filter, "win32text-color",
171                 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
172     var_Get( p_filter, "win32text-color", &val );
173     p_sys->i_font_color = __MAX( __MIN( val.i_int, 0xFFFFFF ), 0 );
174
175     p_sys->hfont = p_sys->hfont_bak = 0;
176     hdc = GetDC( NULL );
177     p_sys->hcdc = CreateCompatibleDC( hdc );
178     p_sys->i_logpy = GetDeviceCaps( hdc, LOGPIXELSY );
179     ReleaseDC( NULL, hdc );
180     SetBkMode( p_sys->hcdc, TRANSPARENT );
181
182     var_Get( p_filter, "win32text-fontsize", &val );
183     p_sys->i_default_font_size = val.i_int;
184     if( SetFont( p_filter, 0 ) != VLC_SUCCESS ) goto error;
185
186     free( psz_fontfile );
187     p_filter->pf_render_text = RenderText;
188     p_filter->pf_render_html = NULL;
189     return VLC_SUCCESS;
190
191  error:
192     free( psz_fontfile );
193     free( p_sys );
194     return VLC_EGENERIC;
195 }
196
197 /*****************************************************************************
198  * Destroy: destroy the module
199  *****************************************************************************/
200 static void Destroy( vlc_object_t *p_this )
201 {
202     filter_t *p_filter = (filter_t *)p_this;
203     filter_sys_t *p_sys = p_filter->p_sys;
204
205     if( p_sys->hfont_bak ) SelectObject( p_sys->hcdc, p_sys->hfont_bak );
206     if( p_sys->hfont ) DeleteObject( p_sys->hfont );
207     DeleteDC( p_sys->hcdc );
208     free( p_sys );
209 }
210
211 /*****************************************************************************
212  * Render: place string in picture
213  *****************************************************************************
214  * This function merges the previously rendered win32text glyphs into a picture
215  *****************************************************************************/
216 static int Render( filter_t *p_filter, subpicture_region_t *p_region,
217                    uint8_t *p_bitmap, int i_width, int i_height )
218 {
219     uint8_t *p_dst;
220     video_format_t fmt;
221     int i, i_pitch;
222     subpicture_region_t *p_region_tmp;
223     vlc_bool_t b_outline = VLC_TRUE;
224
225     /* Create a new subpicture region */
226     memset( &fmt, 0, sizeof(video_format_t) );
227     fmt.i_chroma = VLC_FOURCC('Y','U','V','P');
228     fmt.i_width = fmt.i_visible_width = i_width + (b_outline ? 4 : 0);
229     fmt.i_height = fmt.i_visible_height = i_height + (b_outline ? 4 : 0);
230     fmt.i_x_offset = fmt.i_y_offset = 0;
231     p_region_tmp = spu_CreateRegion( p_filter, &fmt );
232     if( !p_region_tmp )
233     {
234         msg_Err( p_filter, "cannot allocate SPU region" );
235         return VLC_EGENERIC;
236     }
237
238     /* Build palette */
239     fmt.p_palette->i_entries = 16;
240     for( i = 0; i < fmt.p_palette->i_entries; i++ )
241     {
242         fmt.p_palette->palette[i][0] = pi_gamma[i];
243         fmt.p_palette->palette[i][1] = 128;
244         fmt.p_palette->palette[i][2] = 128;
245         fmt.p_palette->palette[i][3] = pi_gamma[i];
246     }
247
248     p_region->fmt = p_region_tmp->fmt;
249     p_region->picture = p_region_tmp->picture;
250     free( p_region_tmp );
251
252     p_dst = p_region->picture.Y_PIXELS;
253     i_pitch = p_region->picture.Y_PITCH;
254
255     if( b_outline )
256     {
257         memset( p_dst, 0, i_pitch * fmt.i_height );
258         p_dst += p_region->picture.Y_PITCH * 2 + 2;
259     }
260
261     for( i = 0; i < i_height; i++ )
262     {
263         memcpy( p_dst, p_bitmap, i_width );
264         p_bitmap += (i_width+3) & ~3;
265         p_dst += i_pitch;
266     }
267
268     /* Outlining (find something better than nearest neighbour filtering ?) */
269     if( b_outline )
270     {
271         uint8_t *p_top = p_dst; /* Use 1st line as a cache */
272         uint8_t left, current;
273         int x, y;
274
275         p_dst = p_region->picture.Y_PIXELS;
276
277         for( y = 1; y < (int)fmt.i_height - 1; y++ )
278         {
279             memcpy( p_top, p_dst, fmt.i_width );
280             p_dst += i_pitch;
281             left = 0;
282
283             for( x = 1; x < (int)fmt.i_width - 1; x++ )
284             {
285                 current = p_dst[x];
286                 p_dst[x] = ( 4 * (int)p_dst[x] + left + p_top[x] + p_dst[x+1] +
287                              p_dst[x + i_pitch]) / 8;
288                 left = current;
289             }
290         }
291         memset( p_top, 0, fmt.i_width );
292     }
293
294     return VLC_SUCCESS;
295 }
296
297 static int RenderText( filter_t *p_filter, subpicture_region_t *p_region_out,
298                        subpicture_region_t *p_region_in )
299 {
300     filter_sys_t *p_sys = p_filter->p_sys;
301     int i_font_color, i_font_alpha, i_font_size;
302     uint8_t *p_bitmap;
303     TCHAR *psz_string;
304     int i, i_width, i_height;
305     HBITMAP bitmap, bitmap_bak;
306     BITMAPINFO *p_bmi;
307     RECT rect = {0};
308     SIZE size;
309
310     /* Sanity check */
311     if( !p_region_in || !p_region_out ) return VLC_EGENERIC;
312 #ifdef UNICODE
313     psz_string = malloc( (strlen( p_region_in->psz_text )+1) * sizeof(TCHAR) );
314     if( mbstowcs( psz_string, p_region_in->psz_text,
315                   strlen( p_region_in->psz_text ) * sizeof(TCHAR) ) < 0 )
316     {
317         free( psz_string );
318         return VLC_EGENERIC;
319     }
320 #else
321     psz_string = strdup( p_region_in->psz_text );
322 #endif
323     if( !psz_string || !*psz_string ) return VLC_EGENERIC;
324
325     if( p_region_in->p_style )
326     {
327         i_font_color = __MAX( __MIN( p_region_in->p_style->i_font_color, 0xFFFFFF ), 0 );
328         i_font_alpha = __MAX( __MIN( p_region_in->p_style->i_font_alpha, 255 ), 0 );
329         i_font_size  = __MAX( __MIN( p_region_in->p_style->i_font_size, 255 ), 0 );
330     }
331     else
332     {
333         i_font_color = p_sys->i_font_color;
334         i_font_alpha = 255 - p_sys->i_font_opacity;
335         i_font_size = p_sys->i_default_font_size;
336     }
337
338     SetFont( p_filter, i_font_size );
339
340     SetTextColor( p_sys->hcdc, RGB( (i_font_color >> 16) & 0xff,
341                   (i_font_color >> 8) & 0xff, i_font_color & 0xff) );
342
343     GetTextExtentExPoint( p_sys->hcdc, psz_string, _tcslen(psz_string),
344                           0, 0, 0, &size );
345     i_width = rect.right = size.cx; i_height = rect.bottom = size.cy;
346
347     p_bmi = malloc(sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD)*16);
348     memset( p_bmi, 0, sizeof(BITMAPINFOHEADER) );
349     p_bmi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
350     p_bmi->bmiHeader.biWidth = (i_width+3) & ~3;
351     p_bmi->bmiHeader.biHeight = - i_height;
352     p_bmi->bmiHeader.biPlanes = 1;
353     p_bmi->bmiHeader.biBitCount = 8;
354     p_bmi->bmiHeader.biCompression = BI_RGB;
355     p_bmi->bmiHeader.biClrUsed = 16;
356
357     for( i = 0; i < 16; i++ )
358     {
359         p_bmi->bmiColors[i].rgbBlue =
360             p_bmi->bmiColors[i].rgbGreen =
361                 p_bmi->bmiColors[i].rgbRed = pi_gamma[i];
362     }
363
364     bitmap = CreateDIBSection( p_sys->hcdc, p_bmi, DIB_RGB_COLORS,
365                                (void **)&p_bitmap, NULL, 0 );
366     if( !bitmap )
367     {
368         msg_Err( p_filter, "could not create bitmap" );
369         return VLC_EGENERIC;
370     }
371
372     bitmap_bak = SelectObject( p_sys->hcdc, bitmap );
373     FillRect( p_sys->hcdc, &rect, (HBRUSH)GetStockObject(BLACK_BRUSH) );
374
375     //TextOut( p_sys->hcdc, 0, 0, psz_string, strlen(psz_string) );
376     if( !DrawText( p_sys->hcdc, psz_string, -1, &rect, 0 ) )
377     {
378         msg_Err( p_filter, "could not draw text" );
379     }
380
381     p_region_out->i_x = p_region_in->i_x;
382     p_region_out->i_y = p_region_in->i_y;
383     Render( p_filter, p_region_out, p_bitmap, i_width, i_height );
384
385     SelectObject( p_sys->hcdc, bitmap_bak );
386     DeleteObject( bitmap );
387     return VLC_SUCCESS;
388 }
389
390 static int SetFont( filter_t *p_filter, int i_size )
391 {
392     filter_sys_t *p_sys = p_filter->p_sys;
393     LOGFONT logfont;
394
395     if( i_size && i_size == p_sys->i_font_size ) return VLC_SUCCESS;
396
397     if( !i_size )
398     {
399         vlc_value_t val;
400
401         if( !p_sys->i_default_font_size &&
402             p_sys->i_display_height == (int)p_filter->fmt_out.video.i_height )
403             return VLC_SUCCESS;
404
405         if( p_sys->i_default_font_size )
406         {
407             i_size = p_sys->i_default_font_size;
408         }
409         else
410         {
411             var_Get( p_filter, "win32text-rel-fontsize", &val );
412             i_size = (int)p_filter->fmt_out.video.i_height / val.i_int;
413             p_filter->p_sys->i_display_height =
414                 p_filter->fmt_out.video.i_height;
415         }
416         if( i_size <= 0 )
417         {
418             msg_Warn( p_filter, "invalid fontsize, using 12" );
419             i_size = 12;
420         }
421
422         msg_Dbg( p_filter, "using fontsize: %i", i_size );
423     }
424
425     p_sys->i_font_size = i_size;
426
427     if( p_sys->hfont_bak ) SelectObject( p_sys->hcdc, p_sys->hfont_bak );
428     if( p_sys->hfont ) DeleteObject( p_sys->hfont );
429
430     i_size = i_size * (int64_t)p_sys->i_logpy / 72;
431
432     logfont.lfHeight = i_size;
433     logfont.lfWidth = 0;
434     logfont.lfEscapement = 0;
435     logfont.lfOrientation = 0;
436     logfont.lfWeight = 0;
437     logfont.lfItalic = FALSE;
438     logfont.lfUnderline = FALSE;
439     logfont.lfStrikeOut = FALSE;
440     logfont.lfCharSet = ANSI_CHARSET;
441     logfont.lfOutPrecision = OUT_DEFAULT_PRECIS;
442     logfont.lfClipPrecision = CLIP_DEFAULT_PRECIS;
443     logfont.lfQuality = ANTIALIASED_QUALITY;
444     logfont.lfPitchAndFamily = DEFAULT_PITCH;
445     memcpy( logfont.lfFaceName, _T("Arial"), sizeof(_T("Arial")) );
446
447     p_sys->hfont = CreateFontIndirect( &logfont );
448
449     p_sys->hfont_bak = SelectObject( p_sys->hcdc, p_sys->hfont );
450
451     return VLC_SUCCESS;
452 }