add_integer: remove callback parameter
[vlc/asuraparaju-public.git] / modules / misc / quartztext.c
blobd3e91c929a7e95d6c0a50c632c32a0c8b6914456
1 /*****************************************************************************
2 * quartztext.c : Put text on the video, using Mac OS X Quartz Engine
3 *****************************************************************************
4 * Copyright (C) 2007, 2009 the VideoLAN team
5 * $Id$
7 * Authors: Bernie Purcell <bitmap@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 //////////////////////////////////////////////////////////////////////////////
25 // Preamble
26 //////////////////////////////////////////////////////////////////////////////
28 #ifdef HAVE_CONFIG_H
29 # include "config.h"
30 #endif
32 #include <vlc_common.h>
33 #include <vlc_plugin.h>
34 #include <vlc_osd.h>
35 #include <vlc_stream.h>
36 #include <vlc_xml.h>
37 #include <vlc_input.h>
39 // Fix ourselves ColorSync headers that gets included in ApplicationServices.
40 #define DisposeCMProfileIterateUPP(a) DisposeCMProfileIterateUPP(CMProfileIterateUPP userUPP __attribute__((unused)))
41 #define DisposeCMMIterateUPP(a) DisposeCMMIterateUPP(CMProfileIterateUPP userUPP __attribute__((unused)))
42 #define __MACHINEEXCEPTIONS__
43 #include <ApplicationServices/ApplicationServices.h>
45 #define DEFAULT_FONT "Arial Black"
46 #define DEFAULT_FONT_COLOR 0xffffff
47 #define DEFAULT_REL_FONT_SIZE 16
49 #define VERTICAL_MARGIN 3
50 #define HORIZONTAL_MARGIN 10
52 //////////////////////////////////////////////////////////////////////////////
53 // Local prototypes
54 //////////////////////////////////////////////////////////////////////////////
55 static int Create ( vlc_object_t * );
56 static void Destroy( vlc_object_t * );
58 static int LoadFontsFromAttachments( filter_t *p_filter );
60 static int RenderText( filter_t *, subpicture_region_t *,
61 subpicture_region_t * );
62 static int RenderHtml( filter_t *, subpicture_region_t *,
63 subpicture_region_t * );
65 static int GetFontSize( filter_t *p_filter );
66 static int RenderYUVA( filter_t *p_filter, subpicture_region_t *p_region,
67 CFMutableAttributedStringRef p_attrString );
69 static void setFontAttibutes( char *psz_fontname, int i_font_size, uint32_t i_font_color,
70 bool b_bold, bool b_italic, bool b_underline,
71 CFRange p_range, CFMutableAttributedStringRef p_attrString );
73 //////////////////////////////////////////////////////////////////////////////
74 // Module descriptor
75 //////////////////////////////////////////////////////////////////////////////
77 // The preferred way to set font style information is for it to come from the
78 // subtitle file, and for it to be rendered with RenderHtml instead of
79 // RenderText.
80 #define FONT_TEXT N_("Font")
81 #define FONT_LONGTEXT N_("Name for the font you want to use")
82 #define FONTSIZER_TEXT N_("Relative font size")
83 #define FONTSIZER_LONGTEXT N_("This is the relative default size of the " \
84 "fonts that will be rendered on the video. If absolute font size is set, "\
85 "relative size will be overridden." )
86 #define COLOR_TEXT N_("Text default color")
87 #define COLOR_LONGTEXT N_("The color of the text that will be rendered on "\
88 "the video. This must be an hexadecimal (like HTML colors). The first two "\
89 "chars are for red, then green, then blue. #000000 = black, #FF0000 = red,"\
90 " #00FF00 = green, #FFFF00 = yellow (red + green), #FFFFFF = white" )
92 static const int pi_color_values[] = {
93 0x00000000, 0x00808080, 0x00C0C0C0, 0x00FFFFFF, 0x00800000,
94 0x00FF0000, 0x00FF00FF, 0x00FFFF00, 0x00808000, 0x00008000, 0x00008080,
95 0x0000FF00, 0x00800080, 0x00000080, 0x000000FF, 0x0000FFFF };
97 static const char *const ppsz_color_descriptions[] = {
98 N_("Black"), N_("Gray"), N_("Silver"), N_("White"), N_("Maroon"),
99 N_("Red"), N_("Fuchsia"), N_("Yellow"), N_("Olive"), N_("Green"), N_("Teal"),
100 N_("Lime"), N_("Purple"), N_("Navy"), N_("Blue"), N_("Aqua") };
102 static const int pi_sizes[] = { 20, 18, 16, 12, 6 };
103 static const char *const ppsz_sizes_text[] = {
104 N_("Smaller"), N_("Small"), N_("Normal"), N_("Large"), N_("Larger") };
106 vlc_module_begin ()
107 set_shortname( N_("Text renderer for Mac"))
108 set_description( N_("CoreText font renderer") )
109 set_category( CAT_VIDEO )
110 set_subcategory( SUBCAT_VIDEO_SUBPIC )
112 add_string( "quartztext-font", DEFAULT_FONT, FONT_TEXT, FONT_LONGTEXT,
113 false )
114 add_integer( "quartztext-rel-fontsize", DEFAULT_REL_FONT_SIZE, FONTSIZER_TEXT,
115 FONTSIZER_LONGTEXT, false )
116 change_integer_list( pi_sizes, ppsz_sizes_text )
117 add_integer( "quartztext-color", 0x00FFFFFF, COLOR_TEXT,
118 COLOR_LONGTEXT, false )
119 change_integer_list( pi_color_values, ppsz_color_descriptions )
120 set_capability( "text renderer", 150 )
121 add_shortcut( "text" )
122 set_callbacks( Create, Destroy )
123 vlc_module_end ()
125 typedef struct font_stack_t font_stack_t;
126 struct font_stack_t
128 char *psz_name;
129 int i_size;
130 uint32_t i_color; // ARGB
132 font_stack_t *p_next;
135 typedef struct
137 int i_font_size;
138 uint32_t i_font_color; /* ARGB */
139 bool b_italic;
140 bool b_bold;
141 bool b_underline;
142 char *psz_fontname;
143 } ft_style_t;
145 typedef struct offscreen_bitmap_t offscreen_bitmap_t;
146 struct offscreen_bitmap_t
148 uint8_t *p_data;
149 int i_bitsPerChannel;
150 int i_bitsPerPixel;
151 int i_bytesPerPixel;
152 int i_bytesPerRow;
155 //////////////////////////////////////////////////////////////////////////////
156 // filter_sys_t: quartztext local data
157 //////////////////////////////////////////////////////////////////////////////
158 // This structure is part of the video output thread descriptor.
159 // It describes the freetype specific properties of an output thread.
160 //////////////////////////////////////////////////////////////////////////////
161 struct filter_sys_t
163 char *psz_font_name;
164 uint8_t i_font_opacity;
165 int i_font_color;
166 int i_font_size;
168 ATSFontContainerRef *p_fonts;
169 int i_fonts;
172 //////////////////////////////////////////////////////////////////////////////
173 // Create: allocates osd-text video thread output method
174 //////////////////////////////////////////////////////////////////////////////
175 // This function allocates and initializes a Clone vout method.
176 //////////////////////////////////////////////////////////////////////////////
177 static int Create( vlc_object_t *p_this )
179 filter_t *p_filter = (filter_t *)p_this;
180 filter_sys_t *p_sys;
182 // Allocate structure
183 p_filter->p_sys = p_sys = malloc( sizeof( filter_sys_t ) );
184 if( !p_sys )
185 return VLC_ENOMEM;
186 p_sys->psz_font_name = var_CreateGetString( p_this, "quartztext-font" );
187 p_sys->i_font_opacity = 255;
188 p_sys->i_font_color = __MAX( __MIN( var_CreateGetInteger( p_this, "quartztext-color" ) , 0xFFFFFF ), 0 );
189 p_sys->i_font_size = GetFontSize( p_filter );
191 p_filter->pf_render_text = RenderText;
192 p_filter->pf_render_html = RenderHtml;
194 p_sys->p_fonts = NULL;
195 p_sys->i_fonts = 0;
197 LoadFontsFromAttachments( p_filter );
199 return VLC_SUCCESS;
202 //////////////////////////////////////////////////////////////////////////////
203 // Destroy: destroy Clone video thread output method
204 //////////////////////////////////////////////////////////////////////////////
205 // Clean up all data and library connections
206 //////////////////////////////////////////////////////////////////////////////
207 static void Destroy( vlc_object_t *p_this )
209 filter_t *p_filter = (filter_t *)p_this;
210 filter_sys_t *p_sys = p_filter->p_sys;
212 if( p_sys->p_fonts )
214 int k;
216 for( k = 0; k < p_sys->i_fonts; k++ )
218 ATSFontDeactivate( p_sys->p_fonts[k], NULL, kATSOptionFlagsDefault );
221 free( p_sys->p_fonts );
224 free( p_sys->psz_font_name );
225 free( p_sys );
228 //////////////////////////////////////////////////////////////////////////////
229 // Make any TTF/OTF fonts present in the attachments of the media file
230 // available to the Quartz engine for text rendering
231 //////////////////////////////////////////////////////////////////////////////
232 static int LoadFontsFromAttachments( filter_t *p_filter )
234 filter_sys_t *p_sys = p_filter->p_sys;
235 input_attachment_t **pp_attachments;
236 int i_attachments_cnt;
238 if( filter_GetInputAttachments( p_filter, &pp_attachments, &i_attachments_cnt ) )
239 return VLC_EGENERIC;
241 p_sys->i_fonts = 0;
242 p_sys->p_fonts = malloc( i_attachments_cnt * sizeof( ATSFontContainerRef ) );
243 if(! p_sys->p_fonts )
244 return VLC_ENOMEM;
246 for( int k = 0; k < i_attachments_cnt; k++ )
248 input_attachment_t *p_attach = pp_attachments[k];
250 if( ( !strcmp( p_attach->psz_mime, "application/x-truetype-font" ) || // TTF
251 !strcmp( p_attach->psz_mime, "application/x-font-otf" ) ) && // OTF
252 p_attach->i_data > 0 && p_attach->p_data )
254 ATSFontContainerRef container;
256 if( noErr == ATSFontActivateFromMemory( p_attach->p_data,
257 p_attach->i_data,
258 kATSFontContextLocal,
259 kATSFontFormatUnspecified,
260 NULL,
261 kATSOptionFlagsDefault,
262 &container ))
264 p_sys->p_fonts[ p_sys->i_fonts++ ] = container;
267 vlc_input_attachment_Delete( p_attach );
269 free( pp_attachments );
271 return VLC_SUCCESS;
274 static char *EliminateCRLF( char *psz_string )
276 char *p;
277 char *q;
279 for( p = psz_string; p && *p; p++ )
281 if( ( *p == '\r' ) && ( *(p+1) == '\n' ) )
283 for( q = p + 1; *q; q++ )
284 *( q - 1 ) = *q;
286 *( q - 1 ) = '\0';
289 return psz_string;
292 // Renders a text subpicture region into another one.
293 // It is used as pf_add_string callback in the vout method by this module
294 static int RenderText( filter_t *p_filter, subpicture_region_t *p_region_out,
295 subpicture_region_t *p_region_in )
297 filter_sys_t *p_sys = p_filter->p_sys;
298 char *psz_string;
299 int i_font_alpha, i_font_size;
300 uint32_t i_font_color;
301 bool b_bold, b_uline, b_italic;
302 vlc_value_t val;
303 int i_scale = 1000;
304 b_bold = b_uline = b_italic = FALSE;
306 p_sys->i_font_size = GetFontSize( p_filter );
308 // Sanity check
309 if( !p_region_in || !p_region_out ) return VLC_EGENERIC;
310 psz_string = p_region_in->psz_text;
311 if( !psz_string || !*psz_string ) return VLC_EGENERIC;
313 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
314 i_scale = val.i_int;
316 if( p_region_in->p_style )
318 i_font_color = __MAX( __MIN( p_region_in->p_style->i_font_color, 0xFFFFFF ), 0 );
319 i_font_alpha = __MAX( __MIN( p_region_in->p_style->i_font_alpha, 255 ), 0 );
320 i_font_size = __MAX( __MIN( p_region_in->p_style->i_font_size, 255 ), 0 ) * i_scale / 1000;
321 if( p_region_in->p_style->i_style_flags )
323 if( p_region_in->p_style->i_style_flags & STYLE_BOLD )
324 b_bold = TRUE;
325 if( p_region_in->p_style->i_style_flags & STYLE_ITALIC )
326 b_italic = TRUE;
327 if( p_region_in->p_style->i_style_flags & STYLE_UNDERLINE )
328 b_uline = TRUE;
331 else
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_font_size;
338 if( !i_font_alpha ) i_font_alpha = 255 - p_sys->i_font_opacity;
340 if( i_font_size <= 0 )
342 msg_Warn( p_filter, "invalid fontsize, using 12" );
343 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
344 i_font_size = 12 * val.i_int / 1000;
345 else
346 i_font_size = 12;
349 p_region_out->i_x = p_region_in->i_x;
350 p_region_out->i_y = p_region_in->i_y;
352 CFMutableAttributedStringRef p_attrString = CFAttributedStringCreateMutable(kCFAllocatorDefault, 0);
354 if( p_attrString )
356 CFStringRef p_cfString;
357 int len;
359 EliminateCRLF( psz_string);
360 p_cfString = CFStringCreateWithCString( NULL, psz_string, kCFStringEncodingUTF8 );
361 CFAttributedStringReplaceString( p_attrString, CFRangeMake(0, 0), p_cfString );
362 CFRelease( p_cfString );
363 len = CFAttributedStringGetLength( p_attrString );
365 setFontAttibutes( p_sys->psz_font_name, i_font_size, i_font_color, b_bold, b_italic, b_uline,
366 CFRangeMake( 0, len ), p_attrString);
368 RenderYUVA( p_filter, p_region_out, p_attrString );
369 CFRelease( p_attrString );
372 return VLC_SUCCESS;
376 static int PushFont( font_stack_t **p_font, const char *psz_name, int i_size,
377 uint32_t i_color )
379 font_stack_t *p_new;
381 if( !p_font )
382 return VLC_EGENERIC;
384 p_new = malloc( sizeof( font_stack_t ) );
385 if( ! p_new )
386 return VLC_ENOMEM;
388 p_new->p_next = NULL;
390 if( psz_name )
391 p_new->psz_name = strdup( psz_name );
392 else
393 p_new->psz_name = NULL;
395 p_new->i_size = i_size;
396 p_new->i_color = i_color;
398 if( !*p_font )
400 *p_font = p_new;
402 else
404 font_stack_t *p_last;
406 for( p_last = *p_font;
407 p_last->p_next;
408 p_last = p_last->p_next )
411 p_last->p_next = p_new;
413 return VLC_SUCCESS;
416 static int PopFont( font_stack_t **p_font )
418 font_stack_t *p_last, *p_next_to_last;
420 if( !p_font || !*p_font )
421 return VLC_EGENERIC;
423 p_next_to_last = NULL;
424 for( p_last = *p_font;
425 p_last->p_next;
426 p_last = p_last->p_next )
428 p_next_to_last = p_last;
431 if( p_next_to_last )
432 p_next_to_last->p_next = NULL;
433 else
434 *p_font = NULL;
436 free( p_last->psz_name );
437 free( p_last );
439 return VLC_SUCCESS;
442 static int PeekFont( font_stack_t **p_font, char **psz_name, int *i_size,
443 uint32_t *i_color )
445 font_stack_t *p_last;
447 if( !p_font || !*p_font )
448 return VLC_EGENERIC;
450 for( p_last=*p_font;
451 p_last->p_next;
452 p_last=p_last->p_next )
455 *psz_name = p_last->psz_name;
456 *i_size = p_last->i_size;
457 *i_color = p_last->i_color;
459 return VLC_SUCCESS;
462 static int HandleFontAttributes( xml_reader_t *p_xml_reader,
463 font_stack_t **p_fonts, int i_scale )
465 int rv;
466 char *psz_fontname = NULL;
467 uint32_t i_font_color = 0xffffff;
468 int i_font_alpha = 0;
469 int i_font_size = 24;
471 // Default all attributes to the top font in the stack -- in case not
472 // all attributes are specified in the sub-font
473 if( VLC_SUCCESS == PeekFont( p_fonts,
474 &psz_fontname,
475 &i_font_size,
476 &i_font_color ))
478 psz_fontname = strdup( psz_fontname );
479 i_font_size = i_font_size * 1000 / i_scale;
481 i_font_alpha = (i_font_color >> 24) & 0xff;
482 i_font_color &= 0x00ffffff;
484 while ( xml_ReaderNextAttr( p_xml_reader ) == VLC_SUCCESS )
486 char *psz_name = xml_ReaderName( p_xml_reader );
487 char *psz_value = xml_ReaderValue( p_xml_reader );
489 if( psz_name && psz_value )
491 if( !strcasecmp( "face", psz_name ) )
493 free( psz_fontname );
494 psz_fontname = strdup( psz_value );
496 else if( !strcasecmp( "size", psz_name ) )
498 if( ( *psz_value == '+' ) || ( *psz_value == '-' ) )
500 int i_value = atoi( psz_value );
502 if( ( i_value >= -5 ) && ( i_value <= 5 ) )
503 i_font_size += ( i_value * i_font_size ) / 10;
504 else if( i_value < -5 )
505 i_font_size = - i_value;
506 else if( i_value > 5 )
507 i_font_size = i_value;
509 else
510 i_font_size = atoi( psz_value );
512 else if( !strcasecmp( "color", psz_name ) &&
513 ( psz_value[0] == '#' ) )
515 i_font_color = strtol( psz_value + 1, NULL, 16 );
516 i_font_color &= 0x00ffffff;
518 else if( !strcasecmp( "alpha", psz_name ) &&
519 ( psz_value[0] == '#' ) )
521 i_font_alpha = strtol( psz_value + 1, NULL, 16 );
522 i_font_alpha &= 0xff;
524 free( psz_name );
525 free( psz_value );
528 rv = PushFont( p_fonts,
529 psz_fontname,
530 i_font_size * i_scale / 1000,
531 (i_font_color & 0xffffff) | ((i_font_alpha & 0xff) << 24) );
533 free( psz_fontname );
535 return rv;
538 static void setFontAttibutes( char *psz_fontname, int i_font_size, uint32_t i_font_color,
539 bool b_bold, bool b_italic, bool b_underline,
540 CFRange p_range, CFMutableAttributedStringRef p_attrString )
542 CFStringRef p_cfString;
543 CTFontRef p_font;
545 // Handle font name and size
546 p_cfString = CFStringCreateWithCString( NULL,
547 psz_fontname,
548 kCFStringEncodingUTF8 );
549 p_font = CTFontCreateWithName( p_cfString,
550 (float)i_font_size,
551 NULL );
552 CFRelease( p_cfString );
553 CFAttributedStringSetAttribute( p_attrString,
554 p_range,
555 kCTFontAttributeName,
556 p_font );
557 CFRelease( p_font );
559 // Handle Underline
560 SInt32 _uline;
561 if( b_underline )
562 _uline = kCTUnderlineStyleSingle;
563 else
564 _uline = kCTUnderlineStyleNone;
566 CFNumberRef underline = CFNumberCreate(NULL, kCFNumberSInt32Type, &_uline);
567 CFAttributedStringSetAttribute( p_attrString,
568 p_range,
569 kCTUnderlineStyleAttributeName,
570 underline );
571 CFRelease( underline );
573 // Handle Bold
574 float _weight;
575 if( b_bold )
576 _weight = 0.5;
577 else
578 _weight = 0.0;
580 CFNumberRef weight = CFNumberCreate(NULL, kCFNumberFloatType, &_weight);
581 CFAttributedStringSetAttribute( p_attrString,
582 p_range,
583 kCTFontWeightTrait,
584 weight );
585 CFRelease( weight );
587 // Handle Italic
588 float _slant;
589 if( b_italic )
590 _slant = 1.0;
591 else
592 _slant = 0.0;
594 CFNumberRef slant = CFNumberCreate(NULL, kCFNumberFloatType, &_slant);
595 CFAttributedStringSetAttribute( p_attrString,
596 p_range,
597 kCTFontSlantTrait,
598 slant );
599 CFRelease( slant );
601 // Handle foreground colour
602 CGColorSpaceRef rgbColorSpace = CGColorSpaceCreateDeviceRGB();
603 CGFloat components[] = { (float)((i_font_color & 0x00ff0000) >> 16) / 255.0,
604 (float)((i_font_color & 0x0000ff00) >> 8) / 255.0,
605 (float)((i_font_color & 0x000000ff) ) / 255.0,
606 (float)(255-((i_font_color & 0xff000000) >> 24)) / 255.0 };
607 CGColorRef fg_text = CGColorCreate(rgbColorSpace, components);
608 CGColorSpaceRelease(rgbColorSpace);
610 CFAttributedStringSetAttribute( p_attrString,
611 p_range,
612 kCTForegroundColorAttributeName,
613 fg_text );
614 CFRelease( fg_text );
618 static void GetAttrStrFromFontStack( font_stack_t **p_fonts,
619 bool b_bold, bool b_italic, bool b_uline,
620 CFRange p_range, CFMutableAttributedStringRef p_attrString )
622 char *psz_fontname = NULL;
623 int i_font_size = 0;
624 uint32_t i_font_color = 0;
626 if( VLC_SUCCESS == PeekFont( p_fonts, &psz_fontname, &i_font_size,
627 &i_font_color ))
629 setFontAttibutes( psz_fontname,
630 i_font_size,
631 i_font_color,
632 b_bold, b_italic, b_uline,
633 p_range,
634 p_attrString );
638 static int ProcessNodes( filter_t *p_filter,
639 xml_reader_t *p_xml_reader,
640 text_style_t *p_font_style,
641 CFMutableAttributedStringRef p_attrString )
643 int rv = VLC_SUCCESS;
644 filter_sys_t *p_sys = p_filter->p_sys;
645 font_stack_t *p_fonts = NULL;
646 vlc_value_t val;
647 int i_scale = 1000;
649 char *psz_node = NULL;
651 bool b_italic = false;
652 bool b_bold = false;
653 bool b_uline = false;
655 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
656 i_scale = val.i_int;
658 if( p_font_style )
660 rv = PushFont( &p_fonts,
661 p_font_style->psz_fontname,
662 p_font_style->i_font_size * i_scale / 1000,
663 (p_font_style->i_font_color & 0xffffff) |
664 ((p_font_style->i_font_alpha & 0xff) << 24) );
666 if( p_font_style->i_style_flags & STYLE_BOLD )
667 b_bold = true;
668 if( p_font_style->i_style_flags & STYLE_ITALIC )
669 b_italic = true;
670 if( p_font_style->i_style_flags & STYLE_UNDERLINE )
671 b_uline = true;
673 else
675 rv = PushFont( &p_fonts,
676 p_sys->psz_font_name,
677 p_sys->i_font_size,
678 p_sys->i_font_color );
680 if( rv != VLC_SUCCESS )
681 return rv;
683 while ( ( xml_ReaderRead( p_xml_reader ) == 1 ) )
685 switch ( xml_ReaderNodeType( p_xml_reader ) )
687 case XML_READER_NONE:
688 break;
689 case XML_READER_ENDELEM:
690 psz_node = xml_ReaderName( p_xml_reader );
692 if( psz_node )
694 if( !strcasecmp( "font", psz_node ) )
695 PopFont( &p_fonts );
696 else if( !strcasecmp( "b", psz_node ) )
697 b_bold = false;
698 else if( !strcasecmp( "i", psz_node ) )
699 b_italic = false;
700 else if( !strcasecmp( "u", psz_node ) )
701 b_uline = false;
703 free( psz_node );
705 break;
706 case XML_READER_STARTELEM:
707 psz_node = xml_ReaderName( p_xml_reader );
708 if( psz_node )
710 if( !strcasecmp( "font", psz_node ) )
711 rv = HandleFontAttributes( p_xml_reader, &p_fonts, i_scale );
712 else if( !strcasecmp( "b", psz_node ) )
713 b_bold = true;
714 else if( !strcasecmp( "i", psz_node ) )
715 b_italic = true;
716 else if( !strcasecmp( "u", psz_node ) )
717 b_uline = true;
718 else if( !strcasecmp( "br", psz_node ) )
720 CFMutableAttributedStringRef p_attrnode = CFAttributedStringCreateMutable(kCFAllocatorDefault, 0);
721 CFAttributedStringReplaceString( p_attrnode, CFRangeMake(0, 0), CFSTR("\n") );
723 GetAttrStrFromFontStack( &p_fonts, b_bold, b_italic, b_uline,
724 CFRangeMake( 0, 1 ),
725 p_attrnode );
726 CFAttributedStringReplaceAttributedString( p_attrString,
727 CFRangeMake(CFAttributedStringGetLength(p_attrString), 0),
728 p_attrnode);
729 CFRelease( p_attrnode );
731 free( psz_node );
733 break;
734 case XML_READER_TEXT:
735 psz_node = xml_ReaderValue( p_xml_reader );
736 if( psz_node )
738 CFStringRef p_cfString;
739 int len;
741 // Turn any multiple-whitespaces into single spaces
742 char *s = strpbrk( psz_node, "\t\r\n " );
743 while( s )
745 int i_whitespace = strspn( s, "\t\r\n " );
747 if( i_whitespace > 1 )
748 memmove( &s[1],
749 &s[i_whitespace],
750 strlen( s ) - i_whitespace + 1 );
751 *s++ = ' ';
753 s = strpbrk( s, "\t\r\n " );
757 CFMutableAttributedStringRef p_attrnode = CFAttributedStringCreateMutable(kCFAllocatorDefault, 0);
758 p_cfString = CFStringCreateWithCString( NULL, psz_node, kCFStringEncodingUTF8 );
759 CFAttributedStringReplaceString( p_attrnode, CFRangeMake(0, 0), p_cfString );
760 CFRelease( p_cfString );
761 len = CFAttributedStringGetLength( p_attrnode );
763 GetAttrStrFromFontStack( &p_fonts, b_bold, b_italic, b_uline,
764 CFRangeMake( 0, len ),
765 p_attrnode );
767 CFAttributedStringReplaceAttributedString( p_attrString,
768 CFRangeMake(CFAttributedStringGetLength(p_attrString), 0),
769 p_attrnode);
770 CFRelease( p_attrnode );
771 free( psz_node );
773 break;
777 while( VLC_SUCCESS == PopFont( &p_fonts ) );
779 return rv;
782 static int RenderHtml( filter_t *p_filter, subpicture_region_t *p_region_out,
783 subpicture_region_t *p_region_in )
785 int rv = VLC_SUCCESS;
786 stream_t *p_sub = NULL;
787 xml_t *p_xml = NULL;
788 xml_reader_t *p_xml_reader = NULL;
790 if( !p_region_in || !p_region_in->psz_html )
791 return VLC_EGENERIC;
793 /* Reset the default fontsize in case screen metrics have changed */
794 p_filter->p_sys->i_font_size = GetFontSize( p_filter );
796 p_sub = stream_MemoryNew( VLC_OBJECT(p_filter),
797 (uint8_t *) p_region_in->psz_html,
798 strlen( p_region_in->psz_html ),
799 true );
800 if( p_sub )
802 p_xml = xml_Create( p_filter );
803 if( p_xml )
805 bool b_karaoke = false;
807 p_xml_reader = xml_ReaderCreate( p_xml, p_sub );
808 if( p_xml_reader )
810 /* Look for Root Node */
811 if( xml_ReaderRead( p_xml_reader ) == 1 )
813 char *psz_node = xml_ReaderName( p_xml_reader );
815 if( !strcasecmp( "karaoke", psz_node ) )
817 /* We're going to have to render the text a number
818 * of times to show the progress marker on the text.
820 var_SetBool( p_filter, "text-rerender", true );
821 b_karaoke = true;
823 else if( !strcasecmp( "text", psz_node ) )
825 b_karaoke = false;
827 else
829 /* Only text and karaoke tags are supported */
830 xml_ReaderDelete( p_xml_reader );
831 p_xml_reader = NULL;
832 rv = VLC_EGENERIC;
835 free( psz_node );
839 if( p_xml_reader )
841 int i_len;
843 CFMutableAttributedStringRef p_attrString = CFAttributedStringCreateMutable(kCFAllocatorDefault, 0);
844 rv = ProcessNodes( p_filter, p_xml_reader,
845 p_region_in->p_style, p_attrString );
847 i_len = CFAttributedStringGetLength( p_attrString );
849 p_region_out->i_x = p_region_in->i_x;
850 p_region_out->i_y = p_region_in->i_y;
852 if(( rv == VLC_SUCCESS ) && ( i_len > 0 ))
854 RenderYUVA( p_filter, p_region_out, p_attrString );
856 CFRelease(p_attrString);
858 xml_ReaderDelete( p_xml_reader );
860 xml_Delete( p_xml );
862 stream_Delete( p_sub );
865 return rv;
868 static CGContextRef CreateOffScreenContext( int i_width, int i_height,
869 offscreen_bitmap_t **pp_memory, CGColorSpaceRef *pp_colorSpace )
871 offscreen_bitmap_t *p_bitmap;
872 CGContextRef p_context = NULL;
874 p_bitmap = (offscreen_bitmap_t *) malloc( sizeof( offscreen_bitmap_t ));
875 if( p_bitmap )
877 p_bitmap->i_bitsPerChannel = 8;
878 p_bitmap->i_bitsPerPixel = 4 * p_bitmap->i_bitsPerChannel; // A,R,G,B
879 p_bitmap->i_bytesPerPixel = p_bitmap->i_bitsPerPixel / 8;
880 p_bitmap->i_bytesPerRow = i_width * p_bitmap->i_bytesPerPixel;
882 p_bitmap->p_data = calloc( i_height, p_bitmap->i_bytesPerRow );
884 *pp_colorSpace = CGColorSpaceCreateWithName( kCGColorSpaceGenericRGB );
886 if( p_bitmap->p_data && *pp_colorSpace )
888 p_context = CGBitmapContextCreate( p_bitmap->p_data, i_width, i_height,
889 p_bitmap->i_bitsPerChannel, p_bitmap->i_bytesPerRow,
890 *pp_colorSpace, kCGImageAlphaPremultipliedFirst);
892 if( p_context )
894 if( CGContextSetAllowsAntialiasing != NULL )
896 CGContextSetAllowsAntialiasing( p_context, true );
899 *pp_memory = p_bitmap;
902 return p_context;
905 static offscreen_bitmap_t *Compose( int i_text_align,
906 CFMutableAttributedStringRef p_attrString,
907 unsigned i_width,
908 unsigned i_height,
909 unsigned *pi_textblock_height )
911 offscreen_bitmap_t *p_offScreen = NULL;
912 CGColorSpaceRef p_colorSpace = NULL;
913 CGContextRef p_context = NULL;
915 p_context = CreateOffScreenContext( i_width, i_height, &p_offScreen, &p_colorSpace );
917 *pi_textblock_height = 0;
918 if( p_context )
920 float horiz_flush;
922 CGContextSetTextMatrix( p_context, CGAffineTransformIdentity );
924 if( i_text_align == SUBPICTURE_ALIGN_RIGHT )
925 horiz_flush = 1.0;
926 else if( i_text_align != SUBPICTURE_ALIGN_LEFT )
927 horiz_flush = 0.5;
928 else
929 horiz_flush = 0.0;
931 // Create the framesetter with the attributed string.
932 CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString(p_attrString);
933 if( framesetter )
935 CTFrameRef frame;
936 CGMutablePathRef p_path = CGPathCreateMutable();
937 CGRect p_bounds = CGRectMake( (float)HORIZONTAL_MARGIN,
938 (float)VERTICAL_MARGIN,
939 (float)(i_width - HORIZONTAL_MARGIN*2),
940 (float)(i_height - VERTICAL_MARGIN *2));
941 CGPathAddRect( p_path, NULL, p_bounds );
943 // Create the frame and draw it into the graphics context
944 frame = CTFramesetterCreateFrame(framesetter, CFRangeMake(0, 0), p_path, NULL);
946 CGPathRelease(p_path);
948 // Set up black outlining of the text --
949 CGContextSetRGBStrokeColor( p_context, 0, 0, 0, 0.5 );
950 CGContextSetTextDrawingMode( p_context, kCGTextFillStroke );
952 if( frame != NULL )
954 CFArrayRef lines;
955 CGPoint penPosition;
957 lines = CTFrameGetLines( frame );
958 penPosition.y = i_height;
959 for (int i=0; i<CFArrayGetCount( lines ); i++)
961 CGFloat ascent, descent, leading;
963 CTLineRef line = (CTLineRef)CFArrayGetValueAtIndex(lines, i);
964 CTLineGetTypographicBounds(line, &ascent, &descent, &leading);
966 // Set the outlining for this line to be dependant on the size of the line -
967 // make it about 5% of the ascent, with a minimum at 1.0
968 float f_thickness = ascent * 0.05;
969 CGContextSetLineWidth( p_context, (( f_thickness > 1.0 ) ? 1.0 : f_thickness ));
971 double penOffset = CTLineGetPenOffsetForFlush(line, horiz_flush, (i_width - HORIZONTAL_MARGIN*2));
972 penPosition.x = HORIZONTAL_MARGIN + penOffset;
973 penPosition.y -= ascent;
974 CGContextSetTextPosition( p_context, penPosition.x, penPosition.y );
975 CTLineDraw( line, p_context );
976 penPosition.y -= descent + leading;
979 *pi_textblock_height = i_height - penPosition.y;
981 CFRelease(frame);
983 CFRelease(framesetter);
985 CGContextFlush( p_context );
986 CGContextRelease( p_context );
988 if( p_colorSpace ) CGColorSpaceRelease( p_colorSpace );
990 return p_offScreen;
993 static int GetFontSize( filter_t *p_filter )
995 return p_filter->fmt_out.video.i_height / DEFAULT_REL_FONT_SIZE;
998 static int RenderYUVA( filter_t *p_filter, subpicture_region_t *p_region,
999 CFMutableAttributedStringRef p_attrString )
1001 offscreen_bitmap_t *p_offScreen = NULL;
1002 unsigned i_textblock_height = 0;
1004 unsigned i_width = p_filter->fmt_out.video.i_visible_width;
1005 unsigned i_height = p_filter->fmt_out.video.i_visible_height;
1006 unsigned i_text_align = p_region->i_align & 0x3;
1008 if( !p_attrString )
1010 msg_Err( p_filter, "Invalid argument to RenderYUVA" );
1011 return VLC_EGENERIC;
1014 p_offScreen = Compose( i_text_align, p_attrString,
1015 i_width, i_height, &i_textblock_height );
1017 if( !p_offScreen )
1019 msg_Err( p_filter, "No offscreen buffer" );
1020 return VLC_EGENERIC;
1023 uint8_t *p_dst_y,*p_dst_u,*p_dst_v,*p_dst_a;
1024 video_format_t fmt;
1025 int i_offset;
1026 unsigned x, y, i_pitch;
1027 uint8_t i_y, i_u, i_v; // YUV values, derived from incoming RGB
1029 // Create a new subpicture region
1030 memset( &fmt, 0, sizeof(video_format_t) );
1031 fmt.i_chroma = VLC_CODEC_YUVA;
1032 fmt.i_width = fmt.i_visible_width = i_width;
1033 fmt.i_height = fmt.i_visible_height = __MIN( i_height, i_textblock_height + VERTICAL_MARGIN * 2);
1034 fmt.i_x_offset = fmt.i_y_offset = 0;
1036 p_region->p_picture = picture_NewFromFormat( &fmt );
1037 if( !p_region->p_picture )
1038 return VLC_EGENERIC;
1039 p_region->fmt = fmt;
1041 p_dst_y = p_region->p_picture->Y_PIXELS;
1042 p_dst_u = p_region->p_picture->U_PIXELS;
1043 p_dst_v = p_region->p_picture->V_PIXELS;
1044 p_dst_a = p_region->p_picture->A_PIXELS;
1045 i_pitch = p_region->p_picture->A_PITCH;
1047 i_offset = (i_height + VERTICAL_MARGIN < fmt.i_height) ? VERTICAL_MARGIN *i_pitch : 0 ;
1048 for( y = 0; y < fmt.i_height; y++)
1050 for( x = 0; x < fmt.i_width; x++)
1052 int i_alpha = p_offScreen->p_data[ y * p_offScreen->i_bytesPerRow + x * p_offScreen->i_bytesPerPixel ];
1053 int i_red = p_offScreen->p_data[ y * p_offScreen->i_bytesPerRow + x * p_offScreen->i_bytesPerPixel + 1 ];
1054 int i_green = p_offScreen->p_data[ y * p_offScreen->i_bytesPerRow + x * p_offScreen->i_bytesPerPixel + 2 ];
1055 int i_blue = p_offScreen->p_data[ y * p_offScreen->i_bytesPerRow + x * p_offScreen->i_bytesPerPixel + 3 ];
1057 i_y = (uint8_t)__MIN(abs( 2104 * i_red + 4130 * i_green +
1058 802 * i_blue + 4096 + 131072 ) >> 13, 235);
1059 i_u = (uint8_t)__MIN(abs( -1214 * i_red + -2384 * i_green +
1060 3598 * i_blue + 4096 + 1048576) >> 13, 240);
1061 i_v = (uint8_t)__MIN(abs( 3598 * i_red + -3013 * i_green +
1062 -585 * i_blue + 4096 + 1048576) >> 13, 240);
1064 p_dst_y[ i_offset + x ] = i_y;
1065 p_dst_u[ i_offset + x ] = i_u;
1066 p_dst_v[ i_offset + x ] = i_v;
1067 p_dst_a[ i_offset + x ] = i_alpha;
1069 i_offset += i_pitch;
1072 free( p_offScreen->p_data );
1073 free( p_offScreen );
1075 return VLC_SUCCESS;