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 #include <TargetConditionals.h>
41 #ifdef TARGET_OS_IPHONE
42 #include <CoreText/CoreText.h>
43 #include <CoreGraphics/CoreGraphics.h>
46 // Fix ourselves ColorSync headers that gets included in ApplicationServices.
47 #define DisposeCMProfileIterateUPP(a) DisposeCMProfileIterateUPP(CMProfileIterateUPP userUPP __attribute__((unused)))
48 #define DisposeCMMIterateUPP(a) DisposeCMMIterateUPP(CMProfileIterateUPP userUPP __attribute__((unused)))
49 #define __MACHINEEXCEPTIONS__
50 #include <ApplicationServices/ApplicationServices.h>
53 #define DEFAULT_FONT "Arial Black"
54 #define DEFAULT_FONT_COLOR 0xffffff
55 #define DEFAULT_REL_FONT_SIZE 16
57 #define VERTICAL_MARGIN 3
58 #define HORIZONTAL_MARGIN 10
60 //////////////////////////////////////////////////////////////////////////////
62 //////////////////////////////////////////////////////////////////////////////
63 static int Create ( vlc_object_t * );
64 static void Destroy( vlc_object_t * );
66 static int LoadFontsFromAttachments( filter_t *p_filter );
68 static int RenderText( filter_t *, subpicture_region_t *,
69 subpicture_region_t * );
70 static int RenderHtml( filter_t *, subpicture_region_t *,
71 subpicture_region_t * );
73 static int GetFontSize( filter_t *p_filter );
74 static int RenderYUVA( filter_t *p_filter, subpicture_region_t *p_region,
75 CFMutableAttributedStringRef p_attrString );
77 static void setFontAttibutes( char *psz_fontname, int i_font_size, uint32_t i_font_color,
78 bool b_bold, bool b_italic, bool b_underline,
79 CFRange p_range, CFMutableAttributedStringRef p_attrString );
81 //////////////////////////////////////////////////////////////////////////////
83 //////////////////////////////////////////////////////////////////////////////
85 // The preferred way to set font style information is for it to come from the
86 // subtitle file, and for it to be rendered with RenderHtml instead of
88 #define FONT_TEXT N_("Font")
89 #define FONT_LONGTEXT N_("Name for the font you want to use")
90 #define FONTSIZER_TEXT N_("Relative font size")
91 #define FONTSIZER_LONGTEXT N_("This is the relative default size of the " \
92 "fonts that will be rendered on the video. If absolute font size is set, "\
93 "relative size will be overridden." )
94 #define COLOR_TEXT N_("Text default color")
95 #define COLOR_LONGTEXT N_("The color of the text that will be rendered on "\
96 "the video. This must be an hexadecimal (like HTML colors). The first two "\
97 "chars are for red, then green, then blue. #000000 = black, #FF0000 = red,"\
98 " #00FF00 = green, #FFFF00 = yellow (red + green), #FFFFFF = white" )
100 static const int pi_color_values[] = {
101 0x00000000, 0x00808080, 0x00C0C0C0, 0x00FFFFFF, 0x00800000,
102 0x00FF0000, 0x00FF00FF, 0x00FFFF00, 0x00808000, 0x00008000, 0x00008080,
103 0x0000FF00, 0x00800080, 0x00000080, 0x000000FF, 0x0000FFFF };
105 static const char *const ppsz_color_descriptions[] = {
106 N_("Black"), N_("Gray"), N_("Silver"), N_("White"), N_("Maroon"),
107 N_("Red"), N_("Fuchsia"), N_("Yellow"), N_("Olive"), N_("Green"), N_("Teal"),
108 N_("Lime"), N_("Purple"), N_("Navy"), N_("Blue"), N_("Aqua") };
110 static const int pi_sizes[] = { 20, 18, 16, 12, 6 };
111 static const char *const ppsz_sizes_text[] = {
112 N_("Smaller"), N_("Small"), N_("Normal"), N_("Large"), N_("Larger") };
115 set_shortname( N_("Text renderer for Mac"))
116 set_description( N_("CoreText font renderer") )
117 set_category( CAT_VIDEO )
118 set_subcategory( SUBCAT_VIDEO_SUBPIC )
120 add_string( "quartztext-font", DEFAULT_FONT, FONT_TEXT, FONT_LONGTEXT,
122 add_integer( "quartztext-rel-fontsize", DEFAULT_REL_FONT_SIZE, FONTSIZER_TEXT,
123 FONTSIZER_LONGTEXT, false )
124 change_integer_list( pi_sizes, ppsz_sizes_text )
125 add_integer( "quartztext-color", 0x00FFFFFF, COLOR_TEXT,
126 COLOR_LONGTEXT, false )
127 change_integer_list( pi_color_values, ppsz_color_descriptions )
128 set_capability( "text renderer", 150 )
129 add_shortcut( "text" )
130 set_callbacks( Create, Destroy )
133 typedef struct font_stack_t font_stack_t;
138 uint32_t i_color; // ARGB
140 font_stack_t *p_next;
146 uint32_t i_font_color; /* ARGB */
153 typedef struct offscreen_bitmap_t offscreen_bitmap_t;
154 struct offscreen_bitmap_t
157 int i_bitsPerChannel;
163 //////////////////////////////////////////////////////////////////////////////
164 // filter_sys_t: quartztext local data
165 //////////////////////////////////////////////////////////////////////////////
166 // This structure is part of the video output thread descriptor.
167 // It describes the freetype specific properties of an output thread.
168 //////////////////////////////////////////////////////////////////////////////
172 uint8_t i_font_opacity;
176 #ifndef TARGET_OS_IPHONE
177 ATSFontContainerRef *p_fonts;
182 //////////////////////////////////////////////////////////////////////////////
183 // Create: allocates osd-text video thread output method
184 //////////////////////////////////////////////////////////////////////////////
185 // This function allocates and initializes a Clone vout method.
186 //////////////////////////////////////////////////////////////////////////////
187 static int Create( vlc_object_t *p_this )
189 filter_t *p_filter = (filter_t *)p_this;
192 // Allocate structure
193 p_filter->p_sys = p_sys = malloc( sizeof( filter_sys_t ) );
196 p_sys->psz_font_name = var_CreateGetString( p_this, "quartztext-font" );
197 p_sys->i_font_opacity = 255;
198 p_sys->i_font_color = __MAX( __MIN( var_CreateGetInteger( p_this, "quartztext-color" ) , 0xFFFFFF ), 0 );
199 p_sys->i_font_size = GetFontSize( p_filter );
201 p_filter->pf_render_text = RenderText;
202 p_filter->pf_render_html = RenderHtml;
204 #ifndef TARGET_OS_IPHONE
205 p_sys->p_fonts = NULL;
209 LoadFontsFromAttachments( p_filter );
214 //////////////////////////////////////////////////////////////////////////////
215 // Destroy: destroy Clone video thread output method
216 //////////////////////////////////////////////////////////////////////////////
217 // Clean up all data and library connections
218 //////////////////////////////////////////////////////////////////////////////
219 static void Destroy( vlc_object_t *p_this )
221 filter_t *p_filter = (filter_t *)p_this;
222 filter_sys_t *p_sys = p_filter->p_sys;
223 #ifndef TARGET_OS_IPHONE
228 for( k = 0; k < p_sys->i_fonts; k++ )
230 ATSFontDeactivate( p_sys->p_fonts[k], NULL, kATSOptionFlagsDefault );
233 free( p_sys->p_fonts );
236 free( p_sys->psz_font_name );
240 //////////////////////////////////////////////////////////////////////////////
241 // Make any TTF/OTF fonts present in the attachments of the media file
242 // available to the Quartz engine for text rendering
243 //////////////////////////////////////////////////////////////////////////////
244 static int LoadFontsFromAttachments( filter_t *p_filter )
246 #ifdef TARGET_OS_IPHONE
247 VLC_UNUSED(p_filter);
250 filter_sys_t *p_sys = p_filter->p_sys;
251 input_attachment_t **pp_attachments;
252 int i_attachments_cnt;
254 if( filter_GetInputAttachments( p_filter, &pp_attachments, &i_attachments_cnt ) )
258 p_sys->p_fonts = malloc( i_attachments_cnt * sizeof( ATSFontContainerRef ) );
259 if(! p_sys->p_fonts )
262 for( int k = 0; k < i_attachments_cnt; k++ )
264 input_attachment_t *p_attach = pp_attachments[k];
266 if( ( !strcmp( p_attach->psz_mime, "application/x-truetype-font" ) || // TTF
267 !strcmp( p_attach->psz_mime, "application/x-font-otf" ) ) && // OTF
268 p_attach->i_data > 0 && p_attach->p_data )
270 ATSFontContainerRef container;
272 if( noErr == ATSFontActivateFromMemory( p_attach->p_data,
274 kATSFontContextLocal,
275 kATSFontFormatUnspecified,
277 kATSOptionFlagsDefault,
280 p_sys->p_fonts[ p_sys->i_fonts++ ] = container;
283 vlc_input_attachment_Delete( p_attach );
285 free( pp_attachments );
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 );
385 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;
488 // Default all attributes to the top font in the stack -- in case not
489 // all attributes are specified in the sub-font
490 if( VLC_SUCCESS == PeekFont( p_fonts,
495 psz_fontname = strdup( psz_fontname );
496 i_font_size = i_font_size * 1000 / i_scale;
498 i_font_alpha = (i_font_color >> 24) & 0xff;
499 i_font_color &= 0x00ffffff;
501 while ( (attr = xml_ReaderNextAttr( p_xml_reader )) )
503 char *psz_value = xml_ReaderValue( p_xml_reader );
508 if( !strcasecmp( "face", attr ) )
510 free( psz_fontname );
511 psz_fontname = strdup( psz_value );
513 else if( !strcasecmp( "size", attr ) )
515 if( ( *psz_value == '+' ) || ( *psz_value == '-' ) )
517 int i_value = atoi( psz_value );
519 if( ( i_value >= -5 ) && ( i_value <= 5 ) )
520 i_font_size += ( i_value * i_font_size ) / 10;
521 else if( i_value < -5 )
522 i_font_size = - i_value;
523 else if( i_value > 5 )
524 i_font_size = i_value;
527 i_font_size = atoi( psz_value );
529 else if( !strcasecmp( "color", attr ) &&
530 ( psz_value[0] == '#' ) )
532 i_font_color = strtol( psz_value + 1, NULL, 16 );
533 i_font_color &= 0x00ffffff;
535 else if( !strcasecmp( "alpha", attr ) &&
536 ( psz_value[0] == '#' ) )
538 i_font_alpha = strtol( psz_value + 1, NULL, 16 );
539 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;
668 bool b_italic = false;
670 bool b_uline = false;
672 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
677 rv = PushFont( &p_fonts,
678 p_font_style->psz_fontname,
679 p_font_style->i_font_size * i_scale / 1000,
680 (p_font_style->i_font_color & 0xffffff) |
681 ((p_font_style->i_font_alpha & 0xff) << 24) );
683 if( p_font_style->i_style_flags & STYLE_BOLD )
685 if( p_font_style->i_style_flags & STYLE_ITALIC )
687 if( p_font_style->i_style_flags & STYLE_UNDERLINE )
692 rv = PushFont( &p_fonts,
693 p_sys->psz_font_name,
695 p_sys->i_font_color );
697 if( rv != VLC_SUCCESS )
700 while ( ( type = xml_ReaderNextNode( p_xml_reader, &node ) ) > 0 )
704 case XML_READER_ENDELEM:
705 if( !strcasecmp( "font", node ) )
707 else if( !strcasecmp( "b", node ) )
709 else if( !strcasecmp( "i", node ) )
711 else if( !strcasecmp( "u", node ) )
715 case XML_READER_STARTELEM:
716 if( !strcasecmp( "font", node ) )
717 rv = HandleFontAttributes( p_xml_reader, &p_fonts, i_scale );
718 else if( !strcasecmp( "b", node ) )
720 else if( !strcasecmp( "i", node ) )
722 else if( !strcasecmp( "u", node ) )
724 else if( !strcasecmp( "br", node ) )
726 CFMutableAttributedStringRef p_attrnode = CFAttributedStringCreateMutable(kCFAllocatorDefault, 0);
727 CFAttributedStringReplaceString( p_attrnode, CFRangeMake(0, 0), CFSTR("\n") );
729 GetAttrStrFromFontStack( &p_fonts, b_bold, b_italic, b_uline,
732 CFAttributedStringReplaceAttributedString( p_attrString,
733 CFRangeMake(CFAttributedStringGetLength(p_attrString), 0),
735 CFRelease( p_attrnode );
738 case XML_READER_TEXT:
740 CFStringRef p_cfString;
743 // Turn any multiple-whitespaces into single spaces
744 char *dup = strdup( node );
747 char *s = strpbrk( dup, "\t\r\n " );
750 int i_whitespace = strspn( s, "\t\r\n " );
752 if( i_whitespace > 1 )
755 strlen( s ) - i_whitespace + 1 );
758 s = strpbrk( s, "\t\r\n " );
762 CFMutableAttributedStringRef p_attrnode = CFAttributedStringCreateMutable(kCFAllocatorDefault, 0);
763 p_cfString = CFStringCreateWithCString( NULL, dup, kCFStringEncodingUTF8 );
764 CFAttributedStringReplaceString( p_attrnode, CFRangeMake(0, 0), p_cfString );
765 CFRelease( p_cfString );
766 len = CFAttributedStringGetLength( p_attrnode );
768 GetAttrStrFromFontStack( &p_fonts, b_bold, b_italic, b_uline,
769 CFRangeMake( 0, len ),
772 CFAttributedStringReplaceAttributedString( p_attrString,
773 CFRangeMake(CFAttributedStringGetLength(p_attrString), 0),
775 CFRelease( p_attrnode );
783 while( VLC_SUCCESS == PopFont( &p_fonts ) );
788 static int RenderHtml( filter_t *p_filter, subpicture_region_t *p_region_out,
789 subpicture_region_t *p_region_in )
791 int rv = VLC_SUCCESS;
792 stream_t *p_sub = NULL;
794 xml_reader_t *p_xml_reader = NULL;
796 if( !p_region_in || !p_region_in->psz_html )
799 /* Reset the default fontsize in case screen metrics have changed */
800 p_filter->p_sys->i_font_size = GetFontSize( p_filter );
802 p_sub = stream_MemoryNew( VLC_OBJECT(p_filter),
803 (uint8_t *) p_region_in->psz_html,
804 strlen( p_region_in->psz_html ),
808 p_xml = xml_Create( p_filter );
811 bool b_karaoke = false;
813 p_xml_reader = xml_ReaderCreate( p_xml, p_sub );
816 /* Look for Root Node */
818 if( xml_ReaderNextNode( p_xml_reader, &name )
819 == XML_READER_STARTELEM )
821 if( !strcasecmp( "karaoke", name ) )
823 /* We're going to have to render the text a number
824 * of times to show the progress marker on the text.
826 var_SetBool( p_filter, "text-rerender", true );
829 else if( !strcasecmp( "text", name ) )
835 /* Only text and karaoke tags are supported */
836 xml_ReaderDelete( p_xml_reader );
847 CFMutableAttributedStringRef p_attrString = CFAttributedStringCreateMutable(kCFAllocatorDefault, 0);
848 rv = ProcessNodes( p_filter, p_xml_reader,
849 p_region_in->p_style, p_attrString );
851 i_len = CFAttributedStringGetLength( p_attrString );
853 p_region_out->i_x = p_region_in->i_x;
854 p_region_out->i_y = p_region_in->i_y;
856 if(( rv == VLC_SUCCESS ) && ( i_len > 0 ))
858 RenderYUVA( p_filter, p_region_out, p_attrString );
860 CFRelease(p_attrString);
862 xml_ReaderDelete( p_xml_reader );
866 stream_Delete( p_sub );
872 static CGContextRef CreateOffScreenContext( int i_width, int i_height,
873 offscreen_bitmap_t **pp_memory, CGColorSpaceRef *pp_colorSpace )
875 offscreen_bitmap_t *p_bitmap;
876 CGContextRef p_context = NULL;
878 p_bitmap = (offscreen_bitmap_t *) malloc( sizeof( offscreen_bitmap_t ));
881 p_bitmap->i_bitsPerChannel = 8;
882 p_bitmap->i_bitsPerPixel = 4 * p_bitmap->i_bitsPerChannel; // A,R,G,B
883 p_bitmap->i_bytesPerPixel = p_bitmap->i_bitsPerPixel / 8;
884 p_bitmap->i_bytesPerRow = i_width * p_bitmap->i_bytesPerPixel;
886 p_bitmap->p_data = calloc( i_height, p_bitmap->i_bytesPerRow );
888 *pp_colorSpace = CGColorSpaceCreateDeviceRGB();
890 if( p_bitmap->p_data && *pp_colorSpace )
892 p_context = CGBitmapContextCreate( p_bitmap->p_data, i_width, i_height,
893 p_bitmap->i_bitsPerChannel, p_bitmap->i_bytesPerRow,
894 *pp_colorSpace, kCGImageAlphaPremultipliedFirst);
898 if( CGContextSetAllowsAntialiasing != NULL )
900 CGContextSetAllowsAntialiasing( p_context, true );
903 *pp_memory = p_bitmap;
909 static offscreen_bitmap_t *Compose( int i_text_align,
910 CFMutableAttributedStringRef p_attrString,
913 unsigned *pi_textblock_height )
915 offscreen_bitmap_t *p_offScreen = NULL;
916 CGColorSpaceRef p_colorSpace = NULL;
917 CGContextRef p_context = NULL;
919 p_context = CreateOffScreenContext( i_width, i_height, &p_offScreen, &p_colorSpace );
921 *pi_textblock_height = 0;
926 CGContextSetTextMatrix( p_context, CGAffineTransformIdentity );
928 if( i_text_align == SUBPICTURE_ALIGN_RIGHT )
930 else if( i_text_align != SUBPICTURE_ALIGN_LEFT )
935 // Create the framesetter with the attributed string.
936 CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString(p_attrString);
940 CGMutablePathRef p_path = CGPathCreateMutable();
941 CGRect p_bounds = CGRectMake( (float)HORIZONTAL_MARGIN,
942 (float)VERTICAL_MARGIN,
943 (float)(i_width - HORIZONTAL_MARGIN*2),
944 (float)(i_height - VERTICAL_MARGIN *2));
945 CGPathAddRect( p_path, NULL, p_bounds );
947 // Create the frame and draw it into the graphics context
948 frame = CTFramesetterCreateFrame(framesetter, CFRangeMake(0, 0), p_path, NULL);
950 CGPathRelease(p_path);
952 // Set up black outlining of the text --
953 CGContextSetRGBStrokeColor( p_context, 0, 0, 0, 0.5 );
954 CGContextSetTextDrawingMode( p_context, kCGTextFillStroke );
961 lines = CTFrameGetLines( frame );
962 penPosition.y = i_height;
963 for (int i=0; i<CFArrayGetCount( lines ); i++)
965 CGFloat ascent, descent, leading;
967 CTLineRef line = (CTLineRef)CFArrayGetValueAtIndex(lines, i);
968 CTLineGetTypographicBounds(line, &ascent, &descent, &leading);
970 // Set the outlining for this line to be dependant on the size of the line -
971 // make it about 5% of the ascent, with a minimum at 1.0
972 float f_thickness = ascent * 0.05;
973 CGContextSetLineWidth( p_context, (( f_thickness > 1.0 ) ? 1.0 : f_thickness ));
975 double penOffset = CTLineGetPenOffsetForFlush(line, horiz_flush, (i_width - HORIZONTAL_MARGIN*2));
976 penPosition.x = HORIZONTAL_MARGIN + penOffset;
977 penPosition.y -= ascent;
978 CGContextSetTextPosition( p_context, penPosition.x, penPosition.y );
979 CTLineDraw( line, p_context );
980 penPosition.y -= descent + leading;
983 *pi_textblock_height = i_height - penPosition.y;
987 CFRelease(framesetter);
989 CGContextFlush( p_context );
990 CGContextRelease( p_context );
992 if( p_colorSpace ) CGColorSpaceRelease( p_colorSpace );
997 static int GetFontSize( filter_t *p_filter )
999 return p_filter->fmt_out.video.i_height / DEFAULT_REL_FONT_SIZE;
1002 static int RenderYUVA( filter_t *p_filter, subpicture_region_t *p_region,
1003 CFMutableAttributedStringRef p_attrString )
1005 offscreen_bitmap_t *p_offScreen = NULL;
1006 unsigned i_textblock_height = 0;
1008 unsigned i_width = p_filter->fmt_out.video.i_visible_width;
1009 unsigned i_height = p_filter->fmt_out.video.i_visible_height;
1010 unsigned i_text_align = p_region->i_align & 0x3;
1014 msg_Err( p_filter, "Invalid argument to RenderYUVA" );
1015 return VLC_EGENERIC;
1018 p_offScreen = Compose( i_text_align, p_attrString,
1019 i_width, i_height, &i_textblock_height );
1023 msg_Err( p_filter, "No offscreen buffer" );
1024 return VLC_EGENERIC;
1027 uint8_t *p_dst_y,*p_dst_u,*p_dst_v,*p_dst_a;
1030 unsigned x, y, i_pitch;
1031 uint8_t i_y, i_u, i_v; // YUV values, derived from incoming RGB
1033 // Create a new subpicture region
1034 memset( &fmt, 0, sizeof(video_format_t) );
1035 fmt.i_chroma = VLC_CODEC_YUVA;
1036 fmt.i_width = fmt.i_visible_width = i_width;
1037 fmt.i_height = fmt.i_visible_height = __MIN( i_height, i_textblock_height + VERTICAL_MARGIN * 2);
1038 fmt.i_x_offset = fmt.i_y_offset = 0;
1040 p_region->p_picture = picture_NewFromFormat( &fmt );
1041 if( !p_region->p_picture )
1042 return VLC_EGENERIC;
1043 p_region->fmt = fmt;
1045 p_dst_y = p_region->p_picture->Y_PIXELS;
1046 p_dst_u = p_region->p_picture->U_PIXELS;
1047 p_dst_v = p_region->p_picture->V_PIXELS;
1048 p_dst_a = p_region->p_picture->A_PIXELS;
1049 i_pitch = p_region->p_picture->A_PITCH;
1051 i_offset = (i_height + VERTICAL_MARGIN < fmt.i_height) ? VERTICAL_MARGIN *i_pitch : 0 ;
1052 for( y = 0; y < fmt.i_height; y++)
1054 for( x = 0; x < fmt.i_width; x++)
1056 int i_alpha = p_offScreen->p_data[ y * p_offScreen->i_bytesPerRow + x * p_offScreen->i_bytesPerPixel ];
1057 int i_red = p_offScreen->p_data[ y * p_offScreen->i_bytesPerRow + x * p_offScreen->i_bytesPerPixel + 1 ];
1058 int i_green = p_offScreen->p_data[ y * p_offScreen->i_bytesPerRow + x * p_offScreen->i_bytesPerPixel + 2 ];
1059 int i_blue = p_offScreen->p_data[ y * p_offScreen->i_bytesPerRow + x * p_offScreen->i_bytesPerPixel + 3 ];
1061 i_y = (uint8_t)__MIN(abs( 2104 * i_red + 4130 * i_green +
1062 802 * i_blue + 4096 + 131072 ) >> 13, 235);
1063 i_u = (uint8_t)__MIN(abs( -1214 * i_red + -2384 * i_green +
1064 3598 * i_blue + 4096 + 1048576) >> 13, 240);
1065 i_v = (uint8_t)__MIN(abs( 3598 * i_red + -3013 * i_green +
1066 -585 * i_blue + 4096 + 1048576) >> 13, 240);
1068 p_dst_y[ i_offset + x ] = i_y;
1069 p_dst_u[ i_offset + x ] = i_u;
1070 p_dst_v[ i_offset + x ] = i_v;
1071 p_dst_a[ i_offset + x ] = i_alpha;
1073 i_offset += i_pitch;
1076 free( p_offScreen->p_data );
1077 free( p_offScreen );