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>
36 #include <vlc_block.h>
37 #include <vlc_filter.h>
38 #include <vlc_stream.h>
40 #include <vlc_input.h>
41 #include <vlc_strings.h>
45 #include <Carbon/Carbon.h>
47 #define DEFAULT_FONT "Arial Black"
48 #define DEFAULT_FONT_COLOR 0xffffff
49 #define DEFAULT_REL_FONT_SIZE 16
51 #define VERTICAL_MARGIN 3
52 #define HORIZONTAL_MARGIN 10
54 //////////////////////////////////////////////////////////////////////////////
56 //////////////////////////////////////////////////////////////////////////////
57 static int Create ( vlc_object_t * );
58 static void Destroy( vlc_object_t * );
60 static int LoadFontsFromAttachments( filter_t *p_filter );
62 static int RenderText( filter_t *, subpicture_region_t *,
63 subpicture_region_t * );
64 static int RenderHtml( filter_t *, subpicture_region_t *,
65 subpicture_region_t * );
67 static int GetFontSize( filter_t *p_filter );
68 static int RenderYUVA( filter_t *p_filter, subpicture_region_t *p_region,
69 CFMutableAttributedStringRef p_attrString );
71 static void setFontAttibutes( char *psz_fontname, int i_font_size, uint32_t i_font_color,
72 bool b_bold, bool b_italic, bool b_underline,
73 CFRange p_range, CFMutableAttributedStringRef p_attrString );
75 //////////////////////////////////////////////////////////////////////////////
77 //////////////////////////////////////////////////////////////////////////////
79 // The preferred way to set font style information is for it to come from the
80 // subtitle file, and for it to be rendered with RenderHtml instead of
82 #define FONT_TEXT N_("Font")
83 #define FONT_LONGTEXT N_("Name for the font you want to use")
84 #define FONTSIZER_TEXT N_("Relative font size")
85 #define FONTSIZER_LONGTEXT N_("This is the relative default size of the " \
86 "fonts that will be rendered on the video. If absolute font size is set, "\
87 "relative size will be overriden." )
88 #define COLOR_TEXT N_("Text default color")
89 #define COLOR_LONGTEXT N_("The color of the text that will be rendered on "\
90 "the video. This must be an hexadecimal (like HTML colors). The first two "\
91 "chars are for red, then green, then blue. #000000 = black, #FF0000 = red,"\
92 " #00FF00 = green, #FFFF00 = yellow (red + green), #FFFFFF = white" )
94 static const int pi_color_values[] = {
95 0x00000000, 0x00808080, 0x00C0C0C0, 0x00FFFFFF, 0x00800000,
96 0x00FF0000, 0x00FF00FF, 0x00FFFF00, 0x00808000, 0x00008000, 0x00008080,
97 0x0000FF00, 0x00800080, 0x00000080, 0x000000FF, 0x0000FFFF };
99 static const char *const ppsz_color_descriptions[] = {
100 N_("Black"), N_("Gray"), N_("Silver"), N_("White"), N_("Maroon"),
101 N_("Red"), N_("Fuchsia"), N_("Yellow"), N_("Olive"), N_("Green"), N_("Teal"),
102 N_("Lime"), N_("Purple"), N_("Navy"), N_("Blue"), N_("Aqua") };
104 static const int pi_sizes[] = { 20, 18, 16, 12, 6 };
105 static const char *const ppsz_sizes_text[] = {
106 N_("Smaller"), N_("Small"), N_("Normal"), N_("Large"), N_("Larger") };
109 set_shortname( N_("Mac Text renderer"))
110 set_description( N_("Quartz font renderer") )
111 set_category( CAT_VIDEO )
112 set_subcategory( SUBCAT_VIDEO_SUBPIC )
114 add_string( "quartztext-font", DEFAULT_FONT, NULL, FONT_TEXT, FONT_LONGTEXT,
116 add_integer( "quartztext-rel-fontsize", DEFAULT_REL_FONT_SIZE, NULL, FONTSIZER_TEXT,
117 FONTSIZER_LONGTEXT, false )
118 change_integer_list( pi_sizes, ppsz_sizes_text, NULL );
119 add_integer( "quartztext-color", 0x00FFFFFF, NULL, COLOR_TEXT,
120 COLOR_LONGTEXT, false )
121 change_integer_list( pi_color_values, ppsz_color_descriptions, NULL );
122 set_capability( "text renderer", 150 )
123 add_shortcut( "text" )
124 set_callbacks( Create, Destroy )
127 typedef struct font_stack_t font_stack_t;
132 uint32_t i_color; // ARGB
134 font_stack_t *p_next;
140 uint32_t i_font_color; /* ARGB */
147 typedef struct offscreen_bitmap_t offscreen_bitmap_t;
148 struct offscreen_bitmap_t
151 int i_bitsPerChannel;
157 //////////////////////////////////////////////////////////////////////////////
158 // filter_sys_t: quartztext local data
159 //////////////////////////////////////////////////////////////////////////////
160 // This structure is part of the video output thread descriptor.
161 // It describes the freetype specific properties of an output thread.
162 //////////////////////////////////////////////////////////////////////////////
166 uint8_t i_font_opacity;
170 ATSFontContainerRef *p_fonts;
174 //////////////////////////////////////////////////////////////////////////////
175 // Create: allocates osd-text video thread output method
176 //////////////////////////////////////////////////////////////////////////////
177 // This function allocates and initializes a Clone vout method.
178 //////////////////////////////////////////////////////////////////////////////
179 static int Create( vlc_object_t *p_this )
181 filter_t *p_filter = (filter_t *)p_this;
184 // Allocate structure
185 p_filter->p_sys = p_sys = malloc( sizeof( filter_sys_t ) );
188 p_sys->psz_font_name = var_CreateGetString( p_this, "quartztext-font" );
189 p_sys->i_font_opacity = 255;
190 p_sys->i_font_color = __MAX( __MIN( var_CreateGetInteger( p_this, "quartztext-color" ) , 0xFFFFFF ), 0 );
191 p_sys->i_font_size = GetFontSize( p_filter );
193 p_filter->pf_render_text = RenderText;
194 p_filter->pf_render_html = RenderHtml;
196 p_sys->p_fonts = NULL;
199 LoadFontsFromAttachments( p_filter );
204 //////////////////////////////////////////////////////////////////////////////
205 // Destroy: destroy Clone video thread output method
206 //////////////////////////////////////////////////////////////////////////////
207 // Clean up all data and library connections
208 //////////////////////////////////////////////////////////////////////////////
209 static void Destroy( vlc_object_t *p_this )
211 filter_t *p_filter = (filter_t *)p_this;
212 filter_sys_t *p_sys = p_filter->p_sys;
218 for( k = 0; k < p_sys->i_fonts; k++ )
220 ATSFontDeactivate( p_sys->p_fonts[k], NULL, kATSOptionFlagsDefault );
223 free( p_sys->p_fonts );
226 free( p_sys->psz_font_name );
230 //////////////////////////////////////////////////////////////////////////////
231 // Make any TTF/OTF fonts present in the attachments of the media file
232 // available to the Quartz engine for text rendering
233 //////////////////////////////////////////////////////////////////////////////
234 static int LoadFontsFromAttachments( filter_t *p_filter )
236 filter_sys_t *p_sys = p_filter->p_sys;
237 input_thread_t *p_input;
238 input_attachment_t **pp_attachments;
239 int i_attachments_cnt;
241 int rv = VLC_SUCCESS;
243 p_input = (input_thread_t *)vlc_object_find( p_filter, VLC_OBJECT_INPUT, FIND_PARENT );
247 if( VLC_SUCCESS != input_Control( p_input, INPUT_GET_ATTACHMENTS, &pp_attachments, &i_attachments_cnt ))
249 vlc_object_release(p_input);
254 p_sys->p_fonts = malloc( i_attachments_cnt * sizeof( ATSFontContainerRef ) );
255 if(! p_sys->p_fonts )
258 for( k = 0; k < i_attachments_cnt; k++ )
260 input_attachment_t *p_attach = pp_attachments[k];
264 if(( !strcmp( p_attach->psz_mime, "application/x-truetype-font" ) || // TTF
265 !strcmp( p_attach->psz_mime, "application/x-font-otf" ) ) && // OTF
266 ( p_attach->i_data > 0 ) &&
267 ( p_attach->p_data != NULL ) )
269 ATSFontContainerRef container;
271 if( noErr == ATSFontActivateFromMemory( p_attach->p_data,
273 kATSFontContextLocal,
274 kATSFontFormatUnspecified,
276 kATSOptionFlagsDefault,
279 p_sys->p_fonts[ p_sys->i_fonts++ ] = container;
283 vlc_input_attachment_Delete( p_attach );
285 free( pp_attachments );
287 vlc_object_release(p_input);
292 static char *EliminateCRLF( char *psz_string )
297 for( p = psz_string; p && *p; p++ )
299 if( ( *p == '\r' ) && ( *(p+1) == '\n' ) )
301 for( q = p + 1; *q; q++ )
310 // Renders a text subpicture region into another one.
311 // It is used as pf_add_string callback in the vout method by this module
312 static int RenderText( filter_t *p_filter, subpicture_region_t *p_region_out,
313 subpicture_region_t *p_region_in )
315 filter_sys_t *p_sys = p_filter->p_sys;
317 int i_font_alpha, i_font_size;
318 uint32_t i_font_color;
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;
340 i_font_color = p_sys->i_font_color;
341 i_font_alpha = 255 - p_sys->i_font_opacity;
342 i_font_size = p_sys->i_font_size;
345 if( !i_font_alpha ) i_font_alpha = 255 - p_sys->i_font_opacity;
347 if( i_font_size <= 0 )
349 msg_Warn( p_filter, "invalid fontsize, using 12" );
350 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
351 i_font_size = 12 * val.i_int / 1000;
356 p_region_out->i_x = p_region_in->i_x;
357 p_region_out->i_y = p_region_in->i_y;
359 CFMutableAttributedStringRef p_attrString = CFAttributedStringCreateMutable(kCFAllocatorDefault, 0);
363 CFStringRef p_cfString;
366 EliminateCRLF( psz_string);
367 p_cfString = CFStringCreateWithCString( NULL, psz_string, kCFStringEncodingUTF8 );
368 CFAttributedStringReplaceString( p_attrString, CFRangeMake(0, 0), p_cfString );
369 CFRelease( p_cfString );
370 len = CFAttributedStringGetLength( p_attrString );
372 setFontAttibutes( p_sys->psz_font_name, i_font_size, i_font_color, FALSE, FALSE, FALSE,
373 CFRangeMake( 0, len ), p_attrString);
375 RenderYUVA( p_filter, p_region_out, p_attrString );
377 CFRelease(p_attrString);
383 static int PushFont( font_stack_t **p_font, const char *psz_name, int i_size,
391 p_new = malloc( sizeof( font_stack_t ) );
395 p_new->p_next = NULL;
398 p_new->psz_name = strdup( psz_name );
400 p_new->psz_name = NULL;
402 p_new->i_size = i_size;
403 p_new->i_color = i_color;
411 font_stack_t *p_last;
413 for( p_last = *p_font;
415 p_last = p_last->p_next )
418 p_last->p_next = p_new;
423 static int PopFont( font_stack_t **p_font )
425 font_stack_t *p_last, *p_next_to_last;
427 if( !p_font || !*p_font )
430 p_next_to_last = NULL;
431 for( p_last = *p_font;
433 p_last = p_last->p_next )
435 p_next_to_last = p_last;
439 p_next_to_last->p_next = NULL;
443 free( p_last->psz_name );
449 static int PeekFont( font_stack_t **p_font, char **psz_name, int *i_size,
452 font_stack_t *p_last;
454 if( !p_font || !*p_font )
459 p_last=p_last->p_next )
462 *psz_name = p_last->psz_name;
463 *i_size = p_last->i_size;
464 *i_color = p_last->i_color;
469 static int HandleFontAttributes( xml_reader_t *p_xml_reader,
470 font_stack_t **p_fonts, int i_scale )
473 char *psz_fontname = NULL;
474 uint32_t i_font_color = 0xffffff;
475 int i_font_alpha = 0;
476 int i_font_size = 24;
478 // Default all attributes to the top font in the stack -- in case not
479 // all attributes are specified in the sub-font
480 if( VLC_SUCCESS == PeekFont( p_fonts,
485 psz_fontname = strdup( psz_fontname );
486 i_font_size = i_font_size * 1000 / i_scale;
488 i_font_alpha = (i_font_color >> 24) & 0xff;
489 i_font_color &= 0x00ffffff;
491 while ( xml_ReaderNextAttr( p_xml_reader ) == VLC_SUCCESS )
493 char *psz_name = xml_ReaderName( p_xml_reader );
494 char *psz_value = xml_ReaderValue( p_xml_reader );
496 if( psz_name && psz_value )
498 if( !strcasecmp( "face", psz_name ) )
500 if( psz_fontname ) free( psz_fontname );
501 psz_fontname = strdup( psz_value );
503 else if( !strcasecmp( "size", psz_name ) )
505 if( ( *psz_value == '+' ) || ( *psz_value == '-' ) )
507 int i_value = atoi( psz_value );
509 if( ( i_value >= -5 ) && ( i_value <= 5 ) )
510 i_font_size += ( i_value * i_font_size ) / 10;
511 else if( i_value < -5 )
512 i_font_size = - i_value;
513 else if( i_value > 5 )
514 i_font_size = i_value;
517 i_font_size = atoi( psz_value );
519 else if( !strcasecmp( "color", psz_name ) &&
520 ( psz_value[0] == '#' ) )
522 i_font_color = strtol( psz_value + 1, NULL, 16 );
523 i_font_color &= 0x00ffffff;
525 else if( !strcasecmp( "alpha", psz_name ) &&
526 ( psz_value[0] == '#' ) )
528 i_font_alpha = strtol( psz_value + 1, NULL, 16 );
529 i_font_alpha &= 0xff;
535 rv = PushFont( p_fonts,
537 i_font_size * i_scale / 1000,
538 (i_font_color & 0xffffff) | ((i_font_alpha & 0xff) << 24) );
540 free( psz_fontname );
545 static void setFontAttibutes( char *psz_fontname, int i_font_size, uint32_t i_font_color,
546 bool b_bold, bool b_italic, bool b_underline,
547 CFRange p_range, CFMutableAttributedStringRef p_attrString )
549 CFStringRef p_cfString;
552 // Handle font name and size
553 p_cfString = CFStringCreateWithCString( NULL,
555 kCFStringEncodingUTF8 );
556 p_font = CTFontCreateWithName( p_cfString,
559 CFRelease( p_cfString );
560 CFAttributedStringSetAttribute( p_attrString,
562 kCTFontAttributeName,
569 _uline = kCTUnderlineStyleSingle;
571 _uline = kCTUnderlineStyleNone;
573 CFNumberRef underline = CFNumberCreate(NULL, kCFNumberSInt32Type, &_uline);
574 CFAttributedStringSetAttribute( p_attrString,
576 kCTUnderlineStyleAttributeName,
578 CFRelease( underline );
587 CFNumberRef weight = CFNumberCreate(NULL, kCFNumberFloatType, &_weight);
588 CFAttributedStringSetAttribute( p_attrString,
601 CFNumberRef slant = CFNumberCreate(NULL, kCFNumberFloatType, &_slant);
602 CFAttributedStringSetAttribute( p_attrString,
608 // Handle foreground colour
609 CGColorSpaceRef rgbColorSpace = CGColorSpaceCreateDeviceRGB();
610 CGFloat components[] = { (float)((i_font_color & 0x00ff0000) >> 16) / 255.0,
611 (float)((i_font_color & 0x0000ff00) >> 8) / 255.0,
612 (float)((i_font_color & 0x000000ff) ) / 255.0,
613 (float)(255-((i_font_color & 0xff000000) >> 24)) / 255.0 };
614 CGColorRef fg_text = CGColorCreate(rgbColorSpace, components);
615 CGColorSpaceRelease(rgbColorSpace);
617 CFAttributedStringSetAttribute( p_attrString,
619 kCTForegroundColorAttributeName,
621 CFRelease( fg_text );
625 static void GetAttrStrFromFontStack( font_stack_t **p_fonts,
626 bool b_bold, bool b_italic, bool b_uline,
627 CFRange p_range, CFMutableAttributedStringRef p_attrString )
629 char *psz_fontname = NULL;
631 uint32_t i_font_color = 0;
633 if( VLC_SUCCESS == PeekFont( p_fonts, &psz_fontname, &i_font_size,
636 setFontAttibutes( psz_fontname,
639 b_bold, b_italic, b_uline,
645 static int ProcessNodes( filter_t *p_filter,
646 xml_reader_t *p_xml_reader,
647 text_style_t *p_font_style,
648 CFMutableAttributedStringRef p_attrString )
650 int rv = VLC_SUCCESS;
651 filter_sys_t *p_sys = p_filter->p_sys;
652 font_stack_t *p_fonts = NULL;
656 char *psz_node = NULL;
658 bool b_italic = false;
660 bool b_uline = false;
662 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
667 rv = PushFont( &p_fonts,
668 p_font_style->psz_fontname,
669 p_font_style->i_font_size * i_scale / 1000,
670 (p_font_style->i_font_color & 0xffffff) |
671 ((p_font_style->i_font_alpha & 0xff) << 24) );
673 if( p_font_style->i_style_flags & STYLE_BOLD )
675 if( p_font_style->i_style_flags & STYLE_ITALIC )
677 if( p_font_style->i_style_flags & STYLE_UNDERLINE )
682 rv = PushFont( &p_fonts,
683 p_sys->psz_font_name,
685 p_sys->i_font_color );
687 if( rv != VLC_SUCCESS )
690 while ( ( xml_ReaderRead( p_xml_reader ) == 1 ) )
692 switch ( xml_ReaderNodeType( p_xml_reader ) )
694 case XML_READER_NONE:
696 case XML_READER_ENDELEM:
697 psz_node = xml_ReaderName( p_xml_reader );
701 if( !strcasecmp( "font", psz_node ) )
703 else if( !strcasecmp( "b", psz_node ) )
705 else if( !strcasecmp( "i", psz_node ) )
707 else if( !strcasecmp( "u", psz_node ) )
713 case XML_READER_STARTELEM:
714 psz_node = xml_ReaderName( p_xml_reader );
717 if( !strcasecmp( "font", psz_node ) )
718 rv = HandleFontAttributes( p_xml_reader, &p_fonts, i_scale );
719 else if( !strcasecmp( "b", psz_node ) )
721 else if( !strcasecmp( "i", psz_node ) )
723 else if( !strcasecmp( "u", psz_node ) )
725 else if( !strcasecmp( "br", psz_node ) )
727 CFMutableAttributedStringRef p_attrnode = CFAttributedStringCreateMutable(kCFAllocatorDefault, 0);
728 CFAttributedStringReplaceString( p_attrnode, CFRangeMake(0, 0), CFSTR("\n") );
730 GetAttrStrFromFontStack( &p_fonts, b_bold, b_italic, b_uline,
733 CFAttributedStringReplaceAttributedString( p_attrString,
734 CFRangeMake(CFAttributedStringGetLength(p_attrString), 0),
736 CFRelease( p_attrnode );
741 case XML_READER_TEXT:
742 psz_node = xml_ReaderValue( p_xml_reader );
745 CFStringRef p_cfString;
748 // Turn any multiple-whitespaces into single spaces
749 char *s = strpbrk( psz_node, "\t\r\n " );
752 int i_whitespace = strspn( s, "\t\r\n " );
754 if( i_whitespace > 1 )
757 strlen( s ) - i_whitespace + 1 );
760 s = strpbrk( s, "\t\r\n " );
764 CFMutableAttributedStringRef p_attrnode = CFAttributedStringCreateMutable(kCFAllocatorDefault, 0);
765 p_cfString = CFStringCreateWithCString( NULL, psz_node, kCFStringEncodingUTF8 );
766 CFAttributedStringReplaceString( p_attrnode, CFRangeMake(0, 0), p_cfString );
767 CFRelease( p_cfString );
768 len = CFAttributedStringGetLength( p_attrnode );
770 GetAttrStrFromFontStack( &p_fonts, b_bold, b_italic, b_uline,
771 CFRangeMake( 0, len ),
774 CFAttributedStringReplaceAttributedString( p_attrString,
775 CFRangeMake(CFAttributedStringGetLength(p_attrString), 0),
777 CFRelease( p_attrnode );
784 while( VLC_SUCCESS == PopFont( &p_fonts ) );
789 static int RenderHtml( filter_t *p_filter, subpicture_region_t *p_region_out,
790 subpicture_region_t *p_region_in )
792 int rv = VLC_SUCCESS;
793 stream_t *p_sub = NULL;
795 xml_reader_t *p_xml_reader = NULL;
797 if( !p_region_in || !p_region_in->psz_html )
800 /* Reset the default fontsize in case screen metrics have changed */
801 p_filter->p_sys->i_font_size = GetFontSize( p_filter );
803 p_sub = stream_MemoryNew( VLC_OBJECT(p_filter),
804 (uint8_t *) p_region_in->psz_html,
805 strlen( p_region_in->psz_html ),
809 p_xml = xml_Create( p_filter );
812 bool b_karaoke = false;
814 p_xml_reader = xml_ReaderCreate( p_xml, p_sub );
817 /* Look for Root Node */
818 if( xml_ReaderRead( p_xml_reader ) == 1 )
820 char *psz_node = xml_ReaderName( p_xml_reader );
822 if( !strcasecmp( "karaoke", psz_node ) )
824 /* We're going to have to render the text a number
825 * of times to show the progress marker on the text.
827 var_SetBool( p_filter, "text-rerender", true );
830 else if( !strcasecmp( "text", psz_node ) )
836 /* Only text and karaoke tags are supported */
837 xml_ReaderDelete( p_xml, p_xml_reader );
850 CFMutableAttributedStringRef p_attrString = CFAttributedStringCreateMutable(kCFAllocatorDefault, 0);
851 rv = ProcessNodes( p_filter, p_xml_reader,
852 p_region_in->p_style, p_attrString );
854 i_len = CFAttributedStringGetLength( p_attrString );
856 p_region_out->i_x = p_region_in->i_x;
857 p_region_out->i_y = p_region_in->i_y;
859 if(( rv == VLC_SUCCESS ) && ( i_len > 0 ))
861 RenderYUVA( p_filter, p_region_out, p_attrString );
863 CFRelease(p_attrString);
865 xml_ReaderDelete( p_xml, p_xml_reader );
869 stream_Delete( p_sub );
875 static CGContextRef CreateOffScreenContext( int i_width, int i_height,
876 offscreen_bitmap_t **pp_memory, CGColorSpaceRef *pp_colorSpace )
878 offscreen_bitmap_t *p_bitmap;
879 CGContextRef p_context = NULL;
881 p_bitmap = (offscreen_bitmap_t *) malloc( sizeof( offscreen_bitmap_t ));
884 p_bitmap->i_bitsPerChannel = 8;
885 p_bitmap->i_bitsPerPixel = 4 * p_bitmap->i_bitsPerChannel; // A,R,G,B
886 p_bitmap->i_bytesPerPixel = p_bitmap->i_bitsPerPixel / 8;
887 p_bitmap->i_bytesPerRow = i_width * p_bitmap->i_bytesPerPixel;
889 p_bitmap->p_data = calloc( i_height, p_bitmap->i_bytesPerRow );
891 *pp_colorSpace = CGColorSpaceCreateWithName( kCGColorSpaceGenericRGB );
893 if( p_bitmap->p_data && *pp_colorSpace )
895 p_context = CGBitmapContextCreate( p_bitmap->p_data, i_width, i_height,
896 p_bitmap->i_bitsPerChannel, p_bitmap->i_bytesPerRow,
897 *pp_colorSpace, kCGImageAlphaPremultipliedFirst);
901 if( CGContextSetAllowsAntialiasing != NULL )
903 CGContextSetAllowsAntialiasing( p_context, true );
906 *pp_memory = p_bitmap;
912 static offscreen_bitmap_t *Compose( int i_text_align,
913 CFMutableAttributedStringRef p_attrString,
916 int *pi_textblock_height )
918 offscreen_bitmap_t *p_offScreen = NULL;
919 CGColorSpaceRef p_colorSpace = NULL;
920 CGContextRef p_context = NULL;
922 p_context = CreateOffScreenContext( i_width, i_height, &p_offScreen, &p_colorSpace );
924 *pi_textblock_height = 0;
929 CGContextSetTextMatrix( p_context, CGAffineTransformIdentity );
931 if( i_text_align == SUBPICTURE_ALIGN_RIGHT )
933 else if( i_text_align != SUBPICTURE_ALIGN_LEFT )
938 // Create the framesetter with the attributed string.
939 CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString(p_attrString);
943 CGMutablePathRef p_path = CGPathCreateMutable();
944 CGRect p_bounds = CGRectMake( (float)HORIZONTAL_MARGIN,
945 (float)VERTICAL_MARGIN,
946 (float)(i_width - HORIZONTAL_MARGIN*2),
947 (float)(i_height - VERTICAL_MARGIN *2));
948 CGPathAddRect( p_path, NULL, p_bounds );
950 // Create the frame and draw it into the graphics context
951 frame = CTFramesetterCreateFrame(framesetter, CFRangeMake(0, 0), p_path, NULL);
953 CGPathRelease(p_path);
955 // Set up black outlining of the text --
956 CGContextSetRGBStrokeColor( p_context, 0, 0, 0, 0.5 );
957 CGContextSetTextDrawingMode( p_context, kCGTextFillStroke );
964 lines = CTFrameGetLines( frame );
965 penPosition.y = i_height;
966 for (int i=0; i<CFArrayGetCount( lines ); i++)
968 CGFloat ascent, descent, leading;
970 CTLineRef line = (CTLineRef)CFArrayGetValueAtIndex(lines, i);
971 CTLineGetTypographicBounds(line, &ascent, &descent, &leading);
973 // Set the outlining for this line to be dependant on the size of the line -
974 // make it about 5% of the ascent, with a minimum at 1.0
975 float f_thickness = ascent * 0.05;
976 CGContextSetLineWidth( p_context, (( f_thickness > 1.0 ) ? 1.0 : f_thickness ));
978 double penOffset = CTLineGetPenOffsetForFlush(line, horiz_flush, (i_width - HORIZONTAL_MARGIN*2));
979 penPosition.x = HORIZONTAL_MARGIN + penOffset;
980 penPosition.y -= ascent;
981 CGContextSetTextPosition( p_context, penPosition.x, penPosition.y );
982 CTLineDraw( line, p_context );
983 penPosition.y -= descent + leading;
986 *pi_textblock_height = i_height - penPosition.y;
990 CFRelease(framesetter);
992 CGContextFlush( p_context );
993 CGContextRelease( p_context );
995 if( p_colorSpace ) CGColorSpaceRelease( p_colorSpace );
1000 static int GetFontSize( filter_t *p_filter )
1002 return p_filter->fmt_out.video.i_height / DEFAULT_REL_FONT_SIZE;
1005 static int RenderYUVA( filter_t *p_filter, subpicture_region_t *p_region,
1006 CFMutableAttributedStringRef p_attrString )
1008 offscreen_bitmap_t *p_offScreen = NULL;
1009 int i_textblock_height = 0;
1011 int i_width = p_filter->fmt_out.video.i_visible_width;
1012 int i_height = p_filter->fmt_out.video.i_visible_height;
1013 int i_text_align = p_region->i_align & 0x3;
1017 msg_Err( p_filter, "Invalid argument to RenderYUVA" );
1018 return VLC_EGENERIC;
1021 p_offScreen = Compose( i_text_align, p_attrString,
1022 i_width, i_height, &i_textblock_height );
1026 msg_Err( p_filter, "No offscreen buffer" );
1027 return VLC_EGENERIC;
1030 uint8_t *p_dst_y,*p_dst_u,*p_dst_v,*p_dst_a;
1032 int x, y, i_offset, i_pitch;
1033 uint8_t i_y, i_u, i_v; // YUV values, derived from incoming RGB
1035 // Create a new subpicture region
1036 memset( &fmt, 0, sizeof(video_format_t) );
1037 fmt.i_chroma = VLC_CODEC_YUVA;
1039 fmt.i_width = fmt.i_visible_width = i_width;
1040 fmt.i_height = fmt.i_visible_height = __MIN( i_height, i_textblock_height + VERTICAL_MARGIN * 2);
1041 fmt.i_x_offset = fmt.i_y_offset = 0;
1043 p_region->p_picture = picture_New( fmt.i_chroma, fmt.i_width, fmt.i_height, fmt.i_aspect );
1044 if( !p_region->p_picture )
1045 return VLC_EGENERIC;
1046 p_region->fmt = fmt;
1048 p_dst_y = p_region->p_picture->Y_PIXELS;
1049 p_dst_u = p_region->p_picture->U_PIXELS;
1050 p_dst_v = p_region->p_picture->V_PIXELS;
1051 p_dst_a = p_region->p_picture->A_PIXELS;
1052 i_pitch = p_region->p_picture->A_PITCH;
1054 i_offset = (i_height+VERTICAL_MARGIN < fmt.i_height) ? VERTICAL_MARGIN *i_pitch : 0 ;
1055 for( y=0; y<fmt.i_height; y++)
1057 for( x=0; x<fmt.i_width; x++)
1059 int i_alpha = p_offScreen->p_data[ y * p_offScreen->i_bytesPerRow + x * p_offScreen->i_bytesPerPixel ];
1060 int i_red = p_offScreen->p_data[ y * p_offScreen->i_bytesPerRow + x * p_offScreen->i_bytesPerPixel + 1 ];
1061 int i_green = p_offScreen->p_data[ y * p_offScreen->i_bytesPerRow + x * p_offScreen->i_bytesPerPixel + 2 ];
1062 int i_blue = p_offScreen->p_data[ y * p_offScreen->i_bytesPerRow + x * p_offScreen->i_bytesPerPixel + 3 ];
1064 i_y = (uint8_t)__MIN(abs( 2104 * i_red + 4130 * i_green +
1065 802 * i_blue + 4096 + 131072 ) >> 13, 235);
1066 i_u = (uint8_t)__MIN(abs( -1214 * i_red + -2384 * i_green +
1067 3598 * i_blue + 4096 + 1048576) >> 13, 240);
1068 i_v = (uint8_t)__MIN(abs( 3598 * i_red + -3013 * i_green +
1069 -585 * i_blue + 4096 + 1048576) >> 13, 240);
1071 p_dst_y[ i_offset + x ] = i_y;
1072 p_dst_u[ i_offset + x ] = i_u;
1073 p_dst_v[ i_offset + x ] = i_v;
1074 p_dst_a[ i_offset + x ] = i_alpha;
1076 i_offset += i_pitch;
1079 free( p_offScreen->p_data );
1080 free( p_offScreen );