1 /*****************************************************************************
2 * quartztext.c : Put text on the video, using Mac OS X Quartz Engine
3 *****************************************************************************
4 * Copyright (C) 2007, 2009 the VideoLAN team
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 //////////////////////////////////////////////////////////////////////////////
26 //////////////////////////////////////////////////////////////////////////////
32 #include <vlc_common.h>
33 #include <vlc_plugin.h>
35 #include <vlc_stream.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 //////////////////////////////////////////////////////////////////////////////
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 //////////////////////////////////////////////////////////////////////////////
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
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 overriden." )
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") };
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
, NULL
, FONT_TEXT
, FONT_LONGTEXT
,
114 add_integer( "quartztext-rel-fontsize", DEFAULT_REL_FONT_SIZE
, NULL
, FONTSIZER_TEXT
,
115 FONTSIZER_LONGTEXT
, false )
116 change_integer_list( pi_sizes
, ppsz_sizes_text
, NULL
);
117 add_integer( "quartztext-color", 0x00FFFFFF, NULL
, COLOR_TEXT
,
118 COLOR_LONGTEXT
, false )
119 change_integer_list( pi_color_values
, ppsz_color_descriptions
, NULL
);
120 set_capability( "text renderer", 150 )
121 add_shortcut( "text" )
122 set_callbacks( Create
, Destroy
)
125 typedef struct font_stack_t font_stack_t
;
130 uint32_t i_color
; // ARGB
132 font_stack_t
*p_next
;
138 uint32_t i_font_color
; /* ARGB */
145 typedef struct offscreen_bitmap_t offscreen_bitmap_t
;
146 struct offscreen_bitmap_t
149 int i_bitsPerChannel
;
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 //////////////////////////////////////////////////////////////////////////////
164 uint8_t i_font_opacity
;
168 ATSFontContainerRef
*p_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
;
182 // Allocate structure
183 p_filter
->p_sys
= p_sys
= malloc( sizeof( filter_sys_t
) );
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
;
197 LoadFontsFromAttachments( p_filter
);
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
;
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
);
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_thread_t
*p_input
;
236 input_attachment_t
**pp_attachments
;
237 int i_attachments_cnt
;
239 int rv
= VLC_SUCCESS
;
241 p_input
= (input_thread_t
*)vlc_object_find( p_filter
, VLC_OBJECT_INPUT
, FIND_PARENT
);
245 if( VLC_SUCCESS
!= input_Control( p_input
, INPUT_GET_ATTACHMENTS
, &pp_attachments
, &i_attachments_cnt
))
247 vlc_object_release(p_input
);
252 p_sys
->p_fonts
= malloc( i_attachments_cnt
* sizeof( ATSFontContainerRef
) );
253 if(! p_sys
->p_fonts
)
256 for( k
= 0; k
< i_attachments_cnt
; k
++ )
258 input_attachment_t
*p_attach
= pp_attachments
[k
];
262 if(( !strcmp( p_attach
->psz_mime
, "application/x-truetype-font" ) || // TTF
263 !strcmp( p_attach
->psz_mime
, "application/x-font-otf" ) ) && // OTF
264 ( p_attach
->i_data
> 0 ) &&
265 ( p_attach
->p_data
!= NULL
) )
267 ATSFontContainerRef container
;
269 if( noErr
== ATSFontActivateFromMemory( p_attach
->p_data
,
271 kATSFontContextLocal
,
272 kATSFontFormatUnspecified
,
274 kATSOptionFlagsDefault
,
277 p_sys
->p_fonts
[ p_sys
->i_fonts
++ ] = container
;
281 vlc_input_attachment_Delete( p_attach
);
283 free( pp_attachments
);
285 vlc_object_release(p_input
);
290 static char *EliminateCRLF( char *psz_string
)
295 for( p
= psz_string
; p
&& *p
; p
++ )
297 if( ( *p
== '\r' ) && ( *(p
+1) == '\n' ) )
299 for( q
= p
+ 1; *q
; q
++ )
308 // Renders a text subpicture region into another one.
309 // It is used as pf_add_string callback in the vout method by this module
310 static int RenderText( filter_t
*p_filter
, subpicture_region_t
*p_region_out
,
311 subpicture_region_t
*p_region_in
)
313 filter_sys_t
*p_sys
= p_filter
->p_sys
;
315 int i_font_alpha
, i_font_size
;
316 uint32_t i_font_color
;
317 bool b_bold
, b_uline
, b_italic
;
320 b_bold
= b_uline
= b_italic
= FALSE
;
322 p_sys
->i_font_size
= GetFontSize( p_filter
);
325 if( !p_region_in
|| !p_region_out
) return VLC_EGENERIC
;
326 psz_string
= p_region_in
->psz_text
;
327 if( !psz_string
|| !*psz_string
) return VLC_EGENERIC
;
329 if( VLC_SUCCESS
== var_Get( p_filter
, "scale", &val
))
332 if( p_region_in
->p_style
)
334 i_font_color
= __MAX( __MIN( p_region_in
->p_style
->i_font_color
, 0xFFFFFF ), 0 );
335 i_font_alpha
= __MAX( __MIN( p_region_in
->p_style
->i_font_alpha
, 255 ), 0 );
336 i_font_size
= __MAX( __MIN( p_region_in
->p_style
->i_font_size
, 255 ), 0 ) * i_scale
/ 1000;
337 if( p_region_in
->p_style
->i_style_flags
)
339 if( p_region_in
->p_style
->i_style_flags
& STYLE_BOLD
)
341 if( p_region_in
->p_style
->i_style_flags
& STYLE_ITALIC
)
343 if( p_region_in
->p_style
->i_style_flags
& STYLE_UNDERLINE
)
349 i_font_color
= p_sys
->i_font_color
;
350 i_font_alpha
= 255 - p_sys
->i_font_opacity
;
351 i_font_size
= p_sys
->i_font_size
;
354 if( !i_font_alpha
) i_font_alpha
= 255 - p_sys
->i_font_opacity
;
356 if( i_font_size
<= 0 )
358 msg_Warn( p_filter
, "invalid fontsize, using 12" );
359 if( VLC_SUCCESS
== var_Get( p_filter
, "scale", &val
))
360 i_font_size
= 12 * val
.i_int
/ 1000;
365 p_region_out
->i_x
= p_region_in
->i_x
;
366 p_region_out
->i_y
= p_region_in
->i_y
;
368 CFMutableAttributedStringRef p_attrString
= CFAttributedStringCreateMutable(kCFAllocatorDefault
, 0);
372 CFStringRef p_cfString
;
375 EliminateCRLF( psz_string
);
376 p_cfString
= CFStringCreateWithCString( NULL
, psz_string
, kCFStringEncodingUTF8
);
377 CFAttributedStringReplaceString( p_attrString
, CFRangeMake(0, 0), p_cfString
);
378 CFRelease( p_cfString
);
379 len
= CFAttributedStringGetLength( p_attrString
);
381 setFontAttibutes( p_sys
->psz_font_name
, i_font_size
, i_font_color
, b_bold
, b_italic
, b_uline
,
382 CFRangeMake( 0, len
), p_attrString
);
384 RenderYUVA( p_filter
, p_region_out
, p_attrString
);
386 CFRelease(p_attrString
);
392 static int PushFont( font_stack_t
**p_font
, const char *psz_name
, int i_size
,
400 p_new
= malloc( sizeof( font_stack_t
) );
404 p_new
->p_next
= NULL
;
407 p_new
->psz_name
= strdup( psz_name
);
409 p_new
->psz_name
= NULL
;
411 p_new
->i_size
= i_size
;
412 p_new
->i_color
= i_color
;
420 font_stack_t
*p_last
;
422 for( p_last
= *p_font
;
424 p_last
= p_last
->p_next
)
427 p_last
->p_next
= p_new
;
432 static int PopFont( font_stack_t
**p_font
)
434 font_stack_t
*p_last
, *p_next_to_last
;
436 if( !p_font
|| !*p_font
)
439 p_next_to_last
= NULL
;
440 for( p_last
= *p_font
;
442 p_last
= p_last
->p_next
)
444 p_next_to_last
= p_last
;
448 p_next_to_last
->p_next
= NULL
;
452 free( p_last
->psz_name
);
458 static int PeekFont( font_stack_t
**p_font
, char **psz_name
, int *i_size
,
461 font_stack_t
*p_last
;
463 if( !p_font
|| !*p_font
)
468 p_last
=p_last
->p_next
)
471 *psz_name
= p_last
->psz_name
;
472 *i_size
= p_last
->i_size
;
473 *i_color
= p_last
->i_color
;
478 static int HandleFontAttributes( xml_reader_t
*p_xml_reader
,
479 font_stack_t
**p_fonts
, int i_scale
)
482 char *psz_fontname
= NULL
;
483 uint32_t i_font_color
= 0xffffff;
484 int i_font_alpha
= 0;
485 int i_font_size
= 24;
487 // Default all attributes to the top font in the stack -- in case not
488 // all attributes are specified in the sub-font
489 if( VLC_SUCCESS
== PeekFont( p_fonts
,
494 psz_fontname
= strdup( psz_fontname
);
495 i_font_size
= i_font_size
* 1000 / i_scale
;
497 i_font_alpha
= (i_font_color
>> 24) & 0xff;
498 i_font_color
&= 0x00ffffff;
500 while ( xml_ReaderNextAttr( p_xml_reader
) == VLC_SUCCESS
)
502 char *psz_name
= xml_ReaderName( p_xml_reader
);
503 char *psz_value
= xml_ReaderValue( p_xml_reader
);
505 if( psz_name
&& psz_value
)
507 if( !strcasecmp( "face", psz_name
) )
509 free( psz_fontname
);
510 psz_fontname
= strdup( psz_value
);
512 else if( !strcasecmp( "size", psz_name
) )
514 if( ( *psz_value
== '+' ) || ( *psz_value
== '-' ) )
516 int i_value
= atoi( psz_value
);
518 if( ( i_value
>= -5 ) && ( i_value
<= 5 ) )
519 i_font_size
+= ( i_value
* i_font_size
) / 10;
520 else if( i_value
< -5 )
521 i_font_size
= - i_value
;
522 else if( i_value
> 5 )
523 i_font_size
= i_value
;
526 i_font_size
= atoi( psz_value
);
528 else if( !strcasecmp( "color", psz_name
) &&
529 ( psz_value
[0] == '#' ) )
531 i_font_color
= strtol( psz_value
+ 1, NULL
, 16 );
532 i_font_color
&= 0x00ffffff;
534 else if( !strcasecmp( "alpha", psz_name
) &&
535 ( psz_value
[0] == '#' ) )
537 i_font_alpha
= strtol( psz_value
+ 1, NULL
, 16 );
538 i_font_alpha
&= 0xff;
544 rv
= PushFont( p_fonts
,
546 i_font_size
* i_scale
/ 1000,
547 (i_font_color
& 0xffffff) | ((i_font_alpha
& 0xff) << 24) );
549 free( psz_fontname
);
554 static void setFontAttibutes( char *psz_fontname
, int i_font_size
, uint32_t i_font_color
,
555 bool b_bold
, bool b_italic
, bool b_underline
,
556 CFRange p_range
, CFMutableAttributedStringRef p_attrString
)
558 CFStringRef p_cfString
;
561 // Handle font name and size
562 p_cfString
= CFStringCreateWithCString( NULL
,
564 kCFStringEncodingUTF8
);
565 p_font
= CTFontCreateWithName( p_cfString
,
568 CFRelease( p_cfString
);
569 CFAttributedStringSetAttribute( p_attrString
,
571 kCTFontAttributeName
,
578 _uline
= kCTUnderlineStyleSingle
;
580 _uline
= kCTUnderlineStyleNone
;
582 CFNumberRef underline
= CFNumberCreate(NULL
, kCFNumberSInt32Type
, &_uline
);
583 CFAttributedStringSetAttribute( p_attrString
,
585 kCTUnderlineStyleAttributeName
,
587 CFRelease( underline
);
596 CFNumberRef weight
= CFNumberCreate(NULL
, kCFNumberFloatType
, &_weight
);
597 CFAttributedStringSetAttribute( p_attrString
,
610 CFNumberRef slant
= CFNumberCreate(NULL
, kCFNumberFloatType
, &_slant
);
611 CFAttributedStringSetAttribute( p_attrString
,
617 // Handle foreground colour
618 CGColorSpaceRef rgbColorSpace
= CGColorSpaceCreateDeviceRGB();
619 CGFloat components
[] = { (float)((i_font_color
& 0x00ff0000) >> 16) / 255.0,
620 (float)((i_font_color
& 0x0000ff00) >> 8) / 255.0,
621 (float)((i_font_color
& 0x000000ff) ) / 255.0,
622 (float)(255-((i_font_color
& 0xff000000) >> 24)) / 255.0 };
623 CGColorRef fg_text
= CGColorCreate(rgbColorSpace
, components
);
624 CGColorSpaceRelease(rgbColorSpace
);
626 CFAttributedStringSetAttribute( p_attrString
,
628 kCTForegroundColorAttributeName
,
630 CFRelease( fg_text
);
634 static void GetAttrStrFromFontStack( font_stack_t
**p_fonts
,
635 bool b_bold
, bool b_italic
, bool b_uline
,
636 CFRange p_range
, CFMutableAttributedStringRef p_attrString
)
638 char *psz_fontname
= NULL
;
640 uint32_t i_font_color
= 0;
642 if( VLC_SUCCESS
== PeekFont( p_fonts
, &psz_fontname
, &i_font_size
,
645 setFontAttibutes( psz_fontname
,
648 b_bold
, b_italic
, b_uline
,
654 static int ProcessNodes( filter_t
*p_filter
,
655 xml_reader_t
*p_xml_reader
,
656 text_style_t
*p_font_style
,
657 CFMutableAttributedStringRef p_attrString
)
659 int rv
= VLC_SUCCESS
;
660 filter_sys_t
*p_sys
= p_filter
->p_sys
;
661 font_stack_t
*p_fonts
= NULL
;
665 char *psz_node
= NULL
;
667 bool b_italic
= false;
669 bool b_uline
= false;
671 if( VLC_SUCCESS
== var_Get( p_filter
, "scale", &val
))
676 rv
= PushFont( &p_fonts
,
677 p_font_style
->psz_fontname
,
678 p_font_style
->i_font_size
* i_scale
/ 1000,
679 (p_font_style
->i_font_color
& 0xffffff) |
680 ((p_font_style
->i_font_alpha
& 0xff) << 24) );
682 if( p_font_style
->i_style_flags
& STYLE_BOLD
)
684 if( p_font_style
->i_style_flags
& STYLE_ITALIC
)
686 if( p_font_style
->i_style_flags
& STYLE_UNDERLINE
)
691 rv
= PushFont( &p_fonts
,
692 p_sys
->psz_font_name
,
694 p_sys
->i_font_color
);
696 if( rv
!= VLC_SUCCESS
)
699 while ( ( xml_ReaderRead( p_xml_reader
) == 1 ) )
701 switch ( xml_ReaderNodeType( p_xml_reader
) )
703 case XML_READER_NONE
:
705 case XML_READER_ENDELEM
:
706 psz_node
= xml_ReaderName( p_xml_reader
);
710 if( !strcasecmp( "font", psz_node
) )
712 else if( !strcasecmp( "b", psz_node
) )
714 else if( !strcasecmp( "i", psz_node
) )
716 else if( !strcasecmp( "u", psz_node
) )
722 case XML_READER_STARTELEM
:
723 psz_node
= xml_ReaderName( p_xml_reader
);
726 if( !strcasecmp( "font", psz_node
) )
727 rv
= HandleFontAttributes( p_xml_reader
, &p_fonts
, i_scale
);
728 else if( !strcasecmp( "b", psz_node
) )
730 else if( !strcasecmp( "i", psz_node
) )
732 else if( !strcasecmp( "u", psz_node
) )
734 else if( !strcasecmp( "br", psz_node
) )
736 CFMutableAttributedStringRef p_attrnode
= CFAttributedStringCreateMutable(kCFAllocatorDefault
, 0);
737 CFAttributedStringReplaceString( p_attrnode
, CFRangeMake(0, 0), CFSTR("\n") );
739 GetAttrStrFromFontStack( &p_fonts
, b_bold
, b_italic
, b_uline
,
742 CFAttributedStringReplaceAttributedString( p_attrString
,
743 CFRangeMake(CFAttributedStringGetLength(p_attrString
), 0),
745 CFRelease( p_attrnode
);
750 case XML_READER_TEXT
:
751 psz_node
= xml_ReaderValue( p_xml_reader
);
754 CFStringRef p_cfString
;
757 // Turn any multiple-whitespaces into single spaces
758 char *s
= strpbrk( psz_node
, "\t\r\n " );
761 int i_whitespace
= strspn( s
, "\t\r\n " );
763 if( i_whitespace
> 1 )
766 strlen( s
) - i_whitespace
+ 1 );
769 s
= strpbrk( s
, "\t\r\n " );
773 CFMutableAttributedStringRef p_attrnode
= CFAttributedStringCreateMutable(kCFAllocatorDefault
, 0);
774 p_cfString
= CFStringCreateWithCString( NULL
, psz_node
, kCFStringEncodingUTF8
);
775 CFAttributedStringReplaceString( p_attrnode
, CFRangeMake(0, 0), p_cfString
);
776 CFRelease( p_cfString
);
777 len
= CFAttributedStringGetLength( p_attrnode
);
779 GetAttrStrFromFontStack( &p_fonts
, b_bold
, b_italic
, b_uline
,
780 CFRangeMake( 0, len
),
783 CFAttributedStringReplaceAttributedString( p_attrString
,
784 CFRangeMake(CFAttributedStringGetLength(p_attrString
), 0),
786 CFRelease( p_attrnode
);
793 while( VLC_SUCCESS
== PopFont( &p_fonts
) );
798 static int RenderHtml( filter_t
*p_filter
, subpicture_region_t
*p_region_out
,
799 subpicture_region_t
*p_region_in
)
801 int rv
= VLC_SUCCESS
;
802 stream_t
*p_sub
= NULL
;
804 xml_reader_t
*p_xml_reader
= NULL
;
806 if( !p_region_in
|| !p_region_in
->psz_html
)
809 /* Reset the default fontsize in case screen metrics have changed */
810 p_filter
->p_sys
->i_font_size
= GetFontSize( p_filter
);
812 p_sub
= stream_MemoryNew( VLC_OBJECT(p_filter
),
813 (uint8_t *) p_region_in
->psz_html
,
814 strlen( p_region_in
->psz_html
),
818 p_xml
= xml_Create( p_filter
);
821 bool b_karaoke
= false;
823 p_xml_reader
= xml_ReaderCreate( p_xml
, p_sub
);
826 /* Look for Root Node */
827 if( xml_ReaderRead( p_xml_reader
) == 1 )
829 char *psz_node
= xml_ReaderName( p_xml_reader
);
831 if( !strcasecmp( "karaoke", psz_node
) )
833 /* We're going to have to render the text a number
834 * of times to show the progress marker on the text.
836 var_SetBool( p_filter
, "text-rerender", true );
839 else if( !strcasecmp( "text", psz_node
) )
845 /* Only text and karaoke tags are supported */
846 xml_ReaderDelete( p_xml
, p_xml_reader
);
859 CFMutableAttributedStringRef p_attrString
= CFAttributedStringCreateMutable(kCFAllocatorDefault
, 0);
860 rv
= ProcessNodes( p_filter
, p_xml_reader
,
861 p_region_in
->p_style
, p_attrString
);
863 i_len
= CFAttributedStringGetLength( p_attrString
);
865 p_region_out
->i_x
= p_region_in
->i_x
;
866 p_region_out
->i_y
= p_region_in
->i_y
;
868 if(( rv
== VLC_SUCCESS
) && ( i_len
> 0 ))
870 RenderYUVA( p_filter
, p_region_out
, p_attrString
);
872 CFRelease(p_attrString
);
874 xml_ReaderDelete( p_xml
, p_xml_reader
);
878 stream_Delete( p_sub
);
884 static CGContextRef
CreateOffScreenContext( int i_width
, int i_height
,
885 offscreen_bitmap_t
**pp_memory
, CGColorSpaceRef
*pp_colorSpace
)
887 offscreen_bitmap_t
*p_bitmap
;
888 CGContextRef p_context
= NULL
;
890 p_bitmap
= (offscreen_bitmap_t
*) malloc( sizeof( offscreen_bitmap_t
));
893 p_bitmap
->i_bitsPerChannel
= 8;
894 p_bitmap
->i_bitsPerPixel
= 4 * p_bitmap
->i_bitsPerChannel
; // A,R,G,B
895 p_bitmap
->i_bytesPerPixel
= p_bitmap
->i_bitsPerPixel
/ 8;
896 p_bitmap
->i_bytesPerRow
= i_width
* p_bitmap
->i_bytesPerPixel
;
898 p_bitmap
->p_data
= calloc( i_height
, p_bitmap
->i_bytesPerRow
);
900 *pp_colorSpace
= CGColorSpaceCreateWithName( kCGColorSpaceGenericRGB
);
902 if( p_bitmap
->p_data
&& *pp_colorSpace
)
904 p_context
= CGBitmapContextCreate( p_bitmap
->p_data
, i_width
, i_height
,
905 p_bitmap
->i_bitsPerChannel
, p_bitmap
->i_bytesPerRow
,
906 *pp_colorSpace
, kCGImageAlphaPremultipliedFirst
);
910 if( CGContextSetAllowsAntialiasing
!= NULL
)
912 CGContextSetAllowsAntialiasing( p_context
, true );
915 *pp_memory
= p_bitmap
;
921 static offscreen_bitmap_t
*Compose( int i_text_align
,
922 CFMutableAttributedStringRef p_attrString
,
925 unsigned *pi_textblock_height
)
927 offscreen_bitmap_t
*p_offScreen
= NULL
;
928 CGColorSpaceRef p_colorSpace
= NULL
;
929 CGContextRef p_context
= NULL
;
931 p_context
= CreateOffScreenContext( i_width
, i_height
, &p_offScreen
, &p_colorSpace
);
933 *pi_textblock_height
= 0;
938 CGContextSetTextMatrix( p_context
, CGAffineTransformIdentity
);
940 if( i_text_align
== SUBPICTURE_ALIGN_RIGHT
)
942 else if( i_text_align
!= SUBPICTURE_ALIGN_LEFT
)
947 // Create the framesetter with the attributed string.
948 CTFramesetterRef framesetter
= CTFramesetterCreateWithAttributedString(p_attrString
);
952 CGMutablePathRef p_path
= CGPathCreateMutable();
953 CGRect p_bounds
= CGRectMake( (float)HORIZONTAL_MARGIN
,
954 (float)VERTICAL_MARGIN
,
955 (float)(i_width
- HORIZONTAL_MARGIN
*2),
956 (float)(i_height
- VERTICAL_MARGIN
*2));
957 CGPathAddRect( p_path
, NULL
, p_bounds
);
959 // Create the frame and draw it into the graphics context
960 frame
= CTFramesetterCreateFrame(framesetter
, CFRangeMake(0, 0), p_path
, NULL
);
962 CGPathRelease(p_path
);
964 // Set up black outlining of the text --
965 CGContextSetRGBStrokeColor( p_context
, 0, 0, 0, 0.5 );
966 CGContextSetTextDrawingMode( p_context
, kCGTextFillStroke
);
973 lines
= CTFrameGetLines( frame
);
974 penPosition
.y
= i_height
;
975 for (int i
=0; i
<CFArrayGetCount( lines
); i
++)
977 CGFloat ascent
, descent
, leading
;
979 CTLineRef line
= (CTLineRef
)CFArrayGetValueAtIndex(lines
, i
);
980 CTLineGetTypographicBounds(line
, &ascent
, &descent
, &leading
);
982 // Set the outlining for this line to be dependant on the size of the line -
983 // make it about 5% of the ascent, with a minimum at 1.0
984 float f_thickness
= ascent
* 0.05;
985 CGContextSetLineWidth( p_context
, (( f_thickness
> 1.0 ) ? 1.0 : f_thickness
));
987 double penOffset
= CTLineGetPenOffsetForFlush(line
, horiz_flush
, (i_width
- HORIZONTAL_MARGIN
*2));
988 penPosition
.x
= HORIZONTAL_MARGIN
+ penOffset
;
989 penPosition
.y
-= ascent
;
990 CGContextSetTextPosition( p_context
, penPosition
.x
, penPosition
.y
);
991 CTLineDraw( line
, p_context
);
992 penPosition
.y
-= descent
+ leading
;
995 *pi_textblock_height
= i_height
- penPosition
.y
;
999 CFRelease(framesetter
);
1001 CGContextFlush( p_context
);
1002 CGContextRelease( p_context
);
1004 if( p_colorSpace
) CGColorSpaceRelease( p_colorSpace
);
1009 static int GetFontSize( filter_t
*p_filter
)
1011 return p_filter
->fmt_out
.video
.i_height
/ DEFAULT_REL_FONT_SIZE
;
1014 static int RenderYUVA( filter_t
*p_filter
, subpicture_region_t
*p_region
,
1015 CFMutableAttributedStringRef p_attrString
)
1017 offscreen_bitmap_t
*p_offScreen
= NULL
;
1018 unsigned i_textblock_height
= 0;
1020 unsigned i_width
= p_filter
->fmt_out
.video
.i_visible_width
;
1021 unsigned i_height
= p_filter
->fmt_out
.video
.i_visible_height
;
1022 unsigned i_text_align
= p_region
->i_align
& 0x3;
1026 msg_Err( p_filter
, "Invalid argument to RenderYUVA" );
1027 return VLC_EGENERIC
;
1030 p_offScreen
= Compose( i_text_align
, p_attrString
,
1031 i_width
, i_height
, &i_textblock_height
);
1035 msg_Err( p_filter
, "No offscreen buffer" );
1036 return VLC_EGENERIC
;
1039 uint8_t *p_dst_y
,*p_dst_u
,*p_dst_v
,*p_dst_a
;
1042 unsigned x
, y
, i_pitch
;
1043 uint8_t i_y
, i_u
, i_v
; // YUV values, derived from incoming RGB
1045 // Create a new subpicture region
1046 memset( &fmt
, 0, sizeof(video_format_t
) );
1047 fmt
.i_chroma
= VLC_CODEC_YUVA
;
1048 fmt
.i_width
= fmt
.i_visible_width
= i_width
;
1049 fmt
.i_height
= fmt
.i_visible_height
= __MIN( i_height
, i_textblock_height
+ VERTICAL_MARGIN
* 2);
1050 fmt
.i_x_offset
= fmt
.i_y_offset
= 0;
1052 p_region
->p_picture
= picture_NewFromFormat( &fmt
);
1053 if( !p_region
->p_picture
)
1054 return VLC_EGENERIC
;
1055 p_region
->fmt
= fmt
;
1057 p_dst_y
= p_region
->p_picture
->Y_PIXELS
;
1058 p_dst_u
= p_region
->p_picture
->U_PIXELS
;
1059 p_dst_v
= p_region
->p_picture
->V_PIXELS
;
1060 p_dst_a
= p_region
->p_picture
->A_PIXELS
;
1061 i_pitch
= p_region
->p_picture
->A_PITCH
;
1063 i_offset
= (i_height
+ VERTICAL_MARGIN
< fmt
.i_height
) ? VERTICAL_MARGIN
*i_pitch
: 0 ;
1064 for( y
= 0; y
< fmt
.i_height
; y
++)
1066 for( x
= 0; x
< fmt
.i_width
; x
++)
1068 int i_alpha
= p_offScreen
->p_data
[ y
* p_offScreen
->i_bytesPerRow
+ x
* p_offScreen
->i_bytesPerPixel
];
1069 int i_red
= p_offScreen
->p_data
[ y
* p_offScreen
->i_bytesPerRow
+ x
* p_offScreen
->i_bytesPerPixel
+ 1 ];
1070 int i_green
= p_offScreen
->p_data
[ y
* p_offScreen
->i_bytesPerRow
+ x
* p_offScreen
->i_bytesPerPixel
+ 2 ];
1071 int i_blue
= p_offScreen
->p_data
[ y
* p_offScreen
->i_bytesPerRow
+ x
* p_offScreen
->i_bytesPerPixel
+ 3 ];
1073 i_y
= (uint8_t)__MIN(abs( 2104 * i_red
+ 4130 * i_green
+
1074 802 * i_blue
+ 4096 + 131072 ) >> 13, 235);
1075 i_u
= (uint8_t)__MIN(abs( -1214 * i_red
+ -2384 * i_green
+
1076 3598 * i_blue
+ 4096 + 1048576) >> 13, 240);
1077 i_v
= (uint8_t)__MIN(abs( 3598 * i_red
+ -3013 * i_green
+
1078 -585 * i_blue
+ 4096 + 1048576) >> 13, 240);
1080 p_dst_y
[ i_offset
+ x
] = i_y
;
1081 p_dst_u
[ i_offset
+ x
] = i_u
;
1082 p_dst_v
[ i_offset
+ x
] = i_v
;
1083 p_dst_a
[ i_offset
+ x
] = i_alpha
;
1085 i_offset
+= i_pitch
;
1088 free( p_offScreen
->p_data
);
1089 free( p_offScreen
);