1 /*****************************************************************************
2 * quartztext.c : Put text on the video, using Mac OS X Quartz Engine
3 *****************************************************************************
4 * Copyright (C) 2007 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 UniChar *psz_utfString, uint32_t i_text_len,
70 uint32_t i_runs, uint32_t *pi_run_lengths,
71 ATSUStyle *pp_styles );
72 static ATSUStyle CreateStyle( char *psz_fontname, int i_font_size,
73 uint32_t i_font_color,
74 bool b_bold, bool b_italic,
76 //////////////////////////////////////////////////////////////////////////////
78 //////////////////////////////////////////////////////////////////////////////
80 // The preferred way to set font style information is for it to come from the
81 // subtitle file, and for it to be rendered with RenderHtml instead of
82 // RenderText. This module, unlike Freetype, doesn't provide any options to
83 // override the fallback font selection used when this style information is
85 #define FONT_TEXT N_("Font")
86 #define FONT_LONGTEXT N_("Name for the font you want to use")
87 #define FONTSIZER_TEXT N_("Relative font size")
88 #define FONTSIZER_LONGTEXT N_("This is the relative default size of the " \
89 "fonts that will be rendered on the video. If absolute font size is set, "\
90 "relative size will be overriden." )
91 #define COLOR_TEXT N_("Text default color")
92 #define COLOR_LONGTEXT N_("The color of the text that will be rendered on "\
93 "the video. This must be an hexadecimal (like HTML colors). The first two "\
94 "chars are for red, then green, then blue. #000000 = black, #FF0000 = red,"\
95 " #00FF00 = green, #FFFF00 = yellow (red + green), #FFFFFF = white" )
97 static const int pi_color_values[] = {
98 0x00000000, 0x00808080, 0x00C0C0C0, 0x00FFFFFF, 0x00800000,
99 0x00FF0000, 0x00FF00FF, 0x00FFFF00, 0x00808000, 0x00008000, 0x00008080,
100 0x0000FF00, 0x00800080, 0x00000080, 0x000000FF, 0x0000FFFF };
102 static const char *const ppsz_color_descriptions[] = {
103 N_("Black"), N_("Gray"), N_("Silver"), N_("White"), N_("Maroon"),
104 N_("Red"), N_("Fuchsia"), N_("Yellow"), N_("Olive"), N_("Green"), N_("Teal"),
105 N_("Lime"), N_("Purple"), N_("Navy"), N_("Blue"), N_("Aqua") };
107 static const int pi_sizes[] = { 20, 18, 16, 12, 6 };
108 static const char *const ppsz_sizes_text[] = {
109 N_("Smaller"), N_("Small"), N_("Normal"), N_("Large"), N_("Larger") };
112 set_shortname( N_("Mac Text renderer"))
113 set_description( N_("Quartz font renderer") )
114 set_category( CAT_VIDEO )
115 set_subcategory( SUBCAT_VIDEO_SUBPIC )
117 add_string( "quartztext-font", DEFAULT_FONT, NULL, FONT_TEXT, FONT_LONGTEXT,
119 add_integer( "quartztext-rel-fontsize", DEFAULT_REL_FONT_SIZE, NULL, FONTSIZER_TEXT,
120 FONTSIZER_LONGTEXT, false );
121 change_integer_list( pi_sizes, ppsz_sizes_text, NULL );
122 add_integer( "quartztext-color", 0x00FFFFFF, NULL, COLOR_TEXT,
123 COLOR_LONGTEXT, false );
124 change_integer_list( pi_color_values, ppsz_color_descriptions, NULL );
125 set_capability( "text renderer", 150 )
126 add_shortcut( "text" )
127 set_callbacks( Create, Destroy )
130 typedef struct offscreen_bitmap_t offscreen_bitmap_t;
131 struct offscreen_bitmap_t
134 int i_bitsPerChannel;
140 //////////////////////////////////////////////////////////////////////////////
141 // filter_sys_t: quartztext local data
142 //////////////////////////////////////////////////////////////////////////////
143 // This structure is part of the video output thread descriptor.
144 // It describes the freetype specific properties of an output thread.
145 //////////////////////////////////////////////////////////////////////////////
149 uint8_t i_font_opacity;
153 ATSFontContainerRef *p_fonts;
157 #define UCHAR UniChar
158 #define TR_DEFAULT_FONT p_sys->psz_font_name
159 #define TR_FONT_STYLE_PTR ATSUStyle
161 #include "text_renderer.h"
163 //////////////////////////////////////////////////////////////////////////////
164 // Create: allocates osd-text video thread output method
165 //////////////////////////////////////////////////////////////////////////////
166 // This function allocates and initializes a Clone vout method.
167 //////////////////////////////////////////////////////////////////////////////
168 static int Create( vlc_object_t *p_this )
170 filter_t *p_filter = (filter_t *)p_this;
173 // Allocate structure
174 p_filter->p_sys = p_sys = malloc( sizeof( filter_sys_t ) );
177 p_sys->psz_font_name = var_CreateGetString( p_this, "quartztext-font" );
178 p_sys->i_font_opacity = 255;
179 p_sys->i_font_color = __MAX( __MIN( var_CreateGetInteger( p_this, "quartztext-color" ) , 0xFFFFFF ), 0 );
180 p_sys->i_font_size = GetFontSize( p_filter );
182 p_filter->pf_render_text = RenderText;
183 p_filter->pf_render_html = RenderHtml;
185 p_sys->p_fonts = NULL;
188 LoadFontsFromAttachments( p_filter );
193 //////////////////////////////////////////////////////////////////////////////
194 // Destroy: destroy Clone video thread output method
195 //////////////////////////////////////////////////////////////////////////////
196 // Clean up all data and library connections
197 //////////////////////////////////////////////////////////////////////////////
198 static void Destroy( vlc_object_t *p_this )
200 filter_t *p_filter = (filter_t *)p_this;
201 filter_sys_t *p_sys = p_filter->p_sys;
207 for( k = 0; k < p_sys->i_fonts; k++ )
209 ATSFontDeactivate( p_sys->p_fonts[k], NULL, kATSOptionFlagsDefault );
212 free( p_sys->p_fonts );
215 free( p_sys->psz_font_name );
219 //////////////////////////////////////////////////////////////////////////////
220 // Make any TTF/OTF fonts present in the attachments of the media file
221 // available to the Quartz engine for text rendering
222 //////////////////////////////////////////////////////////////////////////////
223 static int LoadFontsFromAttachments( filter_t *p_filter )
225 filter_sys_t *p_sys = p_filter->p_sys;
226 input_thread_t *p_input;
227 input_attachment_t **pp_attachments;
228 int i_attachments_cnt;
230 int rv = VLC_SUCCESS;
232 p_input = (input_thread_t *)vlc_object_find( p_filter, VLC_OBJECT_INPUT, FIND_PARENT );
236 if( VLC_SUCCESS != input_Control( p_input, INPUT_GET_ATTACHMENTS, &pp_attachments, &i_attachments_cnt ))
238 vlc_object_release(p_input);
243 p_sys->p_fonts = malloc( i_attachments_cnt * sizeof( ATSFontContainerRef ) );
244 if(! p_sys->p_fonts )
247 for( k = 0; k < i_attachments_cnt; k++ )
249 input_attachment_t *p_attach = pp_attachments[k];
253 if(( !strcmp( p_attach->psz_mime, "application/x-truetype-font" ) || // TTF
254 !strcmp( p_attach->psz_mime, "application/x-font-otf" ) ) && // OTF
255 ( p_attach->i_data > 0 ) &&
256 ( p_attach->p_data != NULL ) )
258 ATSFontContainerRef container;
260 if( noErr == ATSFontActivateFromMemory( p_attach->p_data,
262 kATSFontContextLocal,
263 kATSFontFormatUnspecified,
265 kATSOptionFlagsDefault,
268 p_sys->p_fonts[ p_sys->i_fonts++ ] = container;
272 vlc_input_attachment_Delete( p_attach );
274 free( pp_attachments );
276 vlc_object_release(p_input);
281 #if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_4
282 // Original version of these functions available on:
283 // http://developer.apple.com/documentation/Carbon/Conceptual/QuickDrawToQuartz2D/tq_color/chapter_4_section_3.html
285 #define kGenericRGBProfilePathStr "/System/Library/ColorSync/Profiles/Generic RGB Profile.icc"
287 static CMProfileRef OpenGenericProfile( void )
289 static CMProfileRef cached_rgb_prof = NULL;
291 // Create the profile reference only once
292 if( cached_rgb_prof == NULL )
295 CMProfileLocation loc;
297 loc.locType = cmPathBasedProfile;
298 strcpy( loc.u.pathLoc.path, kGenericRGBProfilePathStr );
300 err = CMOpenProfile( &cached_rgb_prof, &loc );
304 cached_rgb_prof = NULL;
308 if( cached_rgb_prof )
310 // Clone the profile reference so that the caller has
311 // their own reference, not our cached one.
312 CMCloneProfileRef( cached_rgb_prof );
315 return cached_rgb_prof;
318 static CGColorSpaceRef CreateGenericRGBColorSpace( void )
320 static CGColorSpaceRef p_generic_rgb_cs = NULL;
322 if( p_generic_rgb_cs == NULL )
324 CMProfileRef generic_rgb_prof = OpenGenericProfile();
326 if( generic_rgb_prof )
328 p_generic_rgb_cs = CGColorSpaceCreateWithPlatformColorSpace( generic_rgb_prof );
330 CMCloseProfile( generic_rgb_prof );
334 return p_generic_rgb_cs;
338 static char *EliminateCRLF( char *psz_string )
343 for( p = psz_string; p && *p; p++ )
345 if( ( *p == '\r' ) && ( *(p+1) == '\n' ) )
347 for( q = p + 1; *q; q++ )
356 // Convert UTF-8 string to UTF-16 character array -- internal Mac Endian-ness ;
357 // we don't need to worry about bidirectional text conversion as ATSUI should
358 // handle that for us automatically
359 static void ConvertToUTF16( const char *psz_utf8_str, uint32_t *pi_strlen, UniChar **ppsz_utf16_str )
361 CFStringRef p_cfString;
364 p_cfString = CFStringCreateWithCString( NULL, psz_utf8_str, kCFStringEncodingUTF8 );
368 i_string_length = CFStringGetLength( p_cfString );
371 *pi_strlen = i_string_length;
373 if( !*ppsz_utf16_str )
374 *ppsz_utf16_str = (UniChar *) calloc( i_string_length, sizeof( UniChar ) );
376 CFStringGetCharacters( p_cfString, CFRangeMake( 0, i_string_length ), *ppsz_utf16_str );
378 CFRelease( p_cfString );
381 // Renders a text subpicture region into another one.
382 // It is used as pf_add_string callback in the vout method by this module
383 static int RenderText( filter_t *p_filter, subpicture_region_t *p_region_out,
384 subpicture_region_t *p_region_in )
386 filter_sys_t *p_sys = p_filter->p_sys;
387 UniChar *psz_utf16_str = NULL;
388 uint32_t i_string_length;
390 int i_font_color, i_font_alpha, i_font_size;
394 p_sys->i_font_size = GetFontSize( p_filter );
397 if( !p_region_in || !p_region_out ) return VLC_EGENERIC;
398 psz_string = p_region_in->psz_text;
399 if( !psz_string || !*psz_string ) return VLC_EGENERIC;
401 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
404 if( p_region_in->p_style )
406 i_font_color = __MAX( __MIN( p_region_in->p_style->i_font_color, 0xFFFFFF ), 0 );
407 i_font_alpha = __MAX( __MIN( p_region_in->p_style->i_font_alpha, 255 ), 0 );
408 i_font_size = __MAX( __MIN( p_region_in->p_style->i_font_size, 255 ), 0 ) * i_scale / 1000;
412 i_font_color = p_sys->i_font_color;
413 i_font_alpha = 255 - p_sys->i_font_opacity;
414 i_font_size = p_sys->i_font_size;
417 if( !i_font_alpha ) i_font_alpha = 255 - p_sys->i_font_opacity;
419 ConvertToUTF16( EliminateCRLF( psz_string ), &i_string_length, &psz_utf16_str );
421 p_region_out->i_x = p_region_in->i_x;
422 p_region_out->i_y = p_region_in->i_y;
424 if( psz_utf16_str != NULL )
426 ATSUStyle p_style = CreateStyle( p_sys->psz_font_name, i_font_size,
427 (i_font_color & 0xffffff) |
428 ((i_font_alpha & 0xff) << 24),
429 false, false, false );
432 RenderYUVA( p_filter, p_region_out, psz_utf16_str, i_string_length,
433 1, &i_string_length, &p_style );
436 ATSUDisposeStyle( p_style );
437 free( psz_utf16_str );
444 static ATSUStyle CreateStyle( char *psz_fontname, int i_font_size, uint32_t i_font_color,
445 bool b_bold, bool b_italic, bool b_uline )
451 float f_red = (float)(( i_font_color & 0x00FF0000 ) >> 16) / 255.0;
452 float f_green = (float)(( i_font_color & 0x0000FF00 ) >> 8) / 255.0;
453 float f_blue = (float)( i_font_color & 0x000000FF ) / 255.0;
454 float f_alpha = ( 255.0 - (float)(( i_font_color & 0xFF000000 ) >> 24)) / 255.0;
457 Fixed font_size = IntToFixed( i_font_size );
458 ATSURGBAlphaColor font_color = { f_red, f_green, f_blue, f_alpha };
459 Boolean bold = b_bold;
460 Boolean italic = b_italic;
461 Boolean uline = b_uline;
463 ATSUAttributeTag tags[] = { kATSUSizeTag, kATSURGBAlphaColorTag, kATSUQDItalicTag,
464 kATSUQDBoldfaceTag, kATSUQDUnderlineTag, kATSUFontTag };
465 ByteCount sizes[] = { sizeof( Fixed ), sizeof( ATSURGBAlphaColor ), sizeof( Boolean ),
466 sizeof( Boolean ), sizeof( Boolean ), sizeof( ATSUFontID )};
467 ATSUAttributeValuePtr values[] = { &font_size, &font_color, &italic, &bold, &uline, &font };
469 i_tag_cnt = sizeof( tags ) / sizeof( ATSUAttributeTag );
471 status = ATSUFindFontFromName( psz_fontname,
472 strlen( psz_fontname ),
479 if( status != noErr )
481 // If we can't find a suitable font, just do everything else
485 if( noErr == ATSUCreateStyle( &p_style ) )
487 if( noErr == ATSUSetAttributes( p_style, i_tag_cnt, tags, sizes, values ) )
491 ATSUDisposeStyle( p_style );
496 static ATSUStyle GetStyleFromFontStack( filter_sys_t *p_sys,
497 font_stack_t **p_fonts, bool b_bold, bool b_italic,
500 ATSUStyle p_style = NULL;
502 char *psz_fontname = NULL;
503 uint32_t i_font_color = p_sys->i_font_color;
504 uint32_t i_karaoke_bg_color = i_font_color; /* Use it */
505 int i_font_size = p_sys->i_font_size;
507 if( VLC_SUCCESS == PeekFont( p_fonts, &psz_fontname, &i_font_size,
508 &i_font_color, &i_karaoke_bg_color ))
510 p_style = CreateStyle( psz_fontname, i_font_size, i_font_color,
511 b_bold, b_italic, b_uline );
516 static void SetupLine( filter_t *p_filter, const char *psz_text_in,
517 UniChar **psz_text_out, uint32_t *pi_runs,
518 uint32_t **ppi_run_lengths, ATSUStyle **ppp_styles,
521 uint32_t i_string_length = 0;
523 ConvertToUTF16( psz_text_in, &i_string_length, psz_text_out );
524 *psz_text_out += i_string_length;
526 if( ppp_styles && ppi_run_lengths )
531 *ppp_styles = (ATSUStyle *) realloc( *ppp_styles, *pi_runs * sizeof( ATSUStyle ) );
533 *ppp_styles = (ATSUStyle *) malloc( *pi_runs * sizeof( ATSUStyle ) );
535 (*ppp_styles)[ *pi_runs - 1 ] = p_style;
537 if( *ppi_run_lengths )
538 *ppi_run_lengths = (uint32_t *) realloc( *ppi_run_lengths, *pi_runs * sizeof( uint32_t ) );
540 *ppi_run_lengths = (uint32_t *) malloc( *pi_runs * sizeof( uint32_t ) );
542 (*ppi_run_lengths)[ *pi_runs - 1 ] = i_string_length;
546 static int RenderHtml( filter_t *p_filter, subpicture_region_t *p_region_out,
547 subpicture_region_t *p_region_in )
549 int rv = VLC_SUCCESS;
550 stream_t *p_sub = NULL;
552 xml_reader_t *p_xml_reader = NULL;
554 if( !p_region_in || !p_region_in->psz_html )
557 /* Reset the default fontsize in case screen metrics have changed */
558 p_filter->p_sys->i_font_size = GetFontSize( p_filter );
560 p_sub = stream_MemoryNew( VLC_OBJECT(p_filter),
561 (uint8_t *) p_region_in->psz_html,
562 strlen( p_region_in->psz_html ),
566 p_xml = xml_Create( p_filter );
569 bool b_karaoke = false;
571 p_xml_reader = xml_ReaderCreate( p_xml, p_sub );
574 /* Look for Root Node */
575 if( xml_ReaderRead( p_xml_reader ) == 1 )
577 char *psz_node = xml_ReaderName( p_xml_reader );
579 if( !strcasecmp( "karaoke", psz_node ) )
581 /* We're going to have to render the text a number
582 * of times to show the progress marker on the text.
584 var_SetBool( p_filter, "text-rerender", true );
587 else if( !strcasecmp( "text", psz_node ) )
593 /* Only text and karaoke tags are supported */
594 xml_ReaderDelete( p_xml, p_xml_reader );
608 uint32_t i_k_runs = 0;
609 uint32_t *pi_run_lengths = NULL;
610 uint32_t *pi_k_run_lengths = NULL;
611 uint32_t *pi_k_durations = NULL;
612 ATSUStyle *pp_styles = NULL;
614 psz_text = (UniChar *) malloc( strlen( p_region_in->psz_html ) *
620 rv = ProcessNodes( p_filter, p_xml_reader,
621 p_region_in->p_style, psz_text, &i_len,
622 &i_runs, &pi_run_lengths, &pp_styles,
623 /* No karaoke support */
624 false, &i_k_runs, &pi_k_run_lengths, &pi_k_durations );
626 assert( pi_k_run_lengths == NULL && pi_k_durations == NULL );
628 p_region_out->i_x = p_region_in->i_x;
629 p_region_out->i_y = p_region_in->i_y;
631 if(( rv == VLC_SUCCESS ) && ( i_len > 0 ))
633 RenderYUVA( p_filter, p_region_out, psz_text, i_len, i_runs,
634 pi_run_lengths, pp_styles);
637 for( k=0; k<i_runs; k++)
638 ATSUDisposeStyle( pp_styles[k] );
640 free( pi_run_lengths );
644 xml_ReaderDelete( p_xml, p_xml_reader );
648 stream_Delete( p_sub );
654 static CGContextRef CreateOffScreenContext( int i_width, int i_height,
655 offscreen_bitmap_t **pp_memory, CGColorSpaceRef *pp_colorSpace )
657 offscreen_bitmap_t *p_bitmap;
658 CGContextRef p_context = NULL;
660 p_bitmap = (offscreen_bitmap_t *) malloc( sizeof( offscreen_bitmap_t ));
663 p_bitmap->i_bitsPerChannel = 8;
664 p_bitmap->i_bitsPerPixel = 4 * p_bitmap->i_bitsPerChannel; // A,R,G,B
665 p_bitmap->i_bytesPerPixel = p_bitmap->i_bitsPerPixel / 8;
666 p_bitmap->i_bytesPerRow = i_width * p_bitmap->i_bytesPerPixel;
668 p_bitmap->p_data = calloc( i_height, p_bitmap->i_bytesPerRow );
670 #if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_4
671 *pp_colorSpace = CGColorSpaceCreateWithName( kCGColorSpaceGenericRGB );
673 *pp_colorSpace = CreateGenericRGBColorSpace();
676 if( p_bitmap->p_data && *pp_colorSpace )
678 p_context = CGBitmapContextCreate( p_bitmap->p_data, i_width, i_height,
679 p_bitmap->i_bitsPerChannel, p_bitmap->i_bytesPerRow,
680 *pp_colorSpace, kCGImageAlphaPremultipliedFirst);
684 #if MAC_OS_X_VERSION_MIN_REQUIRED > MAC_OS_X_VERSION_10_1
685 // OS X 10.1 doesn't support weak linking of this call which is only available
686 // int 10.4 and later
687 if( CGContextSetAllowsAntialiasing != NULL )
689 CGContextSetAllowsAntialiasing( p_context, true );
693 *pp_memory = p_bitmap;
699 static offscreen_bitmap_t *Compose( int i_text_align, UniChar *psz_utf16_str, uint32_t i_text_len,
700 uint32_t i_runs, uint32_t *pi_run_lengths, ATSUStyle *pp_styles,
701 int i_width, int i_height, int *pi_textblock_height )
703 offscreen_bitmap_t *p_offScreen = NULL;
704 CGColorSpaceRef p_colorSpace = NULL;
705 CGContextRef p_context = NULL;
707 p_context = CreateOffScreenContext( i_width, i_height, &p_offScreen, &p_colorSpace );
711 ATSUTextLayout p_textLayout;
712 OSStatus status = noErr;
714 status = ATSUCreateTextLayoutWithTextPtr( psz_utf16_str, 0, i_text_len, i_text_len,
716 (const UniCharCount *) pi_run_lengths,
719 if( status == noErr )
721 // Attach our offscreen Image Graphics Context to the text style
722 // and setup the line alignment (have to specify the line width
723 // also in order for our chosen alignment to work)
725 Fract alignment = kATSUStartAlignment;
726 Fixed line_width = Long2Fix( i_width - HORIZONTAL_MARGIN * 2 );
728 ATSUAttributeTag tags[] = { kATSUCGContextTag, kATSULineFlushFactorTag, kATSULineWidthTag };
729 ByteCount sizes[] = { sizeof( CGContextRef ), sizeof( Fract ), sizeof( Fixed ) };
730 ATSUAttributeValuePtr values[] = { &p_context, &alignment, &line_width };
732 int i_tag_cnt = sizeof( tags ) / sizeof( ATSUAttributeTag );
734 if( i_text_align == SUBPICTURE_ALIGN_RIGHT )
736 alignment = kATSUEndAlignment;
738 else if( i_text_align != SUBPICTURE_ALIGN_LEFT )
740 alignment = kATSUCenterAlignment;
743 ATSUSetLayoutControls( p_textLayout, i_tag_cnt, tags, sizes, values );
745 // let ATSUI deal with characters not-in-our-specified-font
746 ATSUSetTransientFontMatching( p_textLayout, true );
748 Fixed x = Long2Fix( HORIZONTAL_MARGIN );
749 Fixed y = Long2Fix( i_height );
751 // Set the line-breaks and draw individual lines
752 uint32_t i_start = 0;
753 uint32_t i_end = i_text_len;
755 // Set up black outlining of the text --
756 CGContextSetRGBStrokeColor( p_context, 0, 0, 0, 0.5 );
757 CGContextSetTextDrawingMode( p_context, kCGTextFillStroke );
758 CGContextSetShadow( p_context, CGSizeMake( 0, 0 ), 5 );
759 float black_components[4] = {0, 0, 0, 1};
760 CGContextSetShadowWithColor (p_context, CGSizeMake( 0, 0 ), 5, CGColorCreate( CGColorSpaceCreateWithName( kCGColorSpaceGenericRGB ), black_components ));
763 // ATSUBreakLine will automatically pick up any manual '\n's also
764 status = ATSUBreakLine( p_textLayout, i_start, line_width, true, (UniCharArrayOffset *) &i_end );
765 if( ( status == noErr ) || ( status == kATSULineBreakInWord ) )
769 uint32_t i_actualSize;
771 // Come down far enough to fit the height of this line --
772 ATSUGetLineControl( p_textLayout, i_start, kATSULineAscentTag,
773 sizeof( Fixed ), &ascent, (ByteCount *) &i_actualSize );
775 // Quartz uses an upside-down co-ordinate space -> y values decrease as
776 // you move down the page
779 // Set the outlining for this line to be dependent on the size of the line -
780 // make it about 5% of the ascent, with a minimum at 1.0
781 float f_thickness = FixedToFloat( ascent ) * 0.05;
782 CGContextSetLineWidth( p_context, (( f_thickness < 1.0 ) ? 1.0 : f_thickness ));
783 ATSUDrawText( p_textLayout, i_start, i_end - i_start, x, y );
785 // and now prepare for the next line by coming down far enough for our
787 ATSUGetLineControl( p_textLayout, i_start, kATSULineDescentTag,
788 sizeof( Fixed ), &descent, (ByteCount *) &i_actualSize );
796 while( i_end < i_text_len );
798 *pi_textblock_height = i_height - Fix2Long( y );
799 CGContextFlush( p_context );
801 ATSUDisposeTextLayout( p_textLayout );
804 CGContextRelease( p_context );
806 if( p_colorSpace ) CGColorSpaceRelease( p_colorSpace );
811 static int GetFontSize( filter_t *p_filter )
813 return p_filter->fmt_out.video.i_height / __MAX(1, var_CreateGetInteger( p_filter, "quartztext-rel-fontsize" ));
816 static int RenderYUVA( filter_t *p_filter, subpicture_region_t *p_region, UniChar *psz_utf16_str,
817 uint32_t i_text_len, uint32_t i_runs, uint32_t *pi_run_lengths, ATSUStyle *pp_styles )
819 offscreen_bitmap_t *p_offScreen = NULL;
820 int i_textblock_height = 0;
822 int i_width = p_filter->fmt_out.video.i_visible_width;
823 int i_height = p_filter->fmt_out.video.i_visible_height;
824 int i_text_align = p_region->i_align & 0x3;
828 msg_Err( p_filter, "Invalid argument to RenderYUVA" );
832 p_offScreen = Compose( i_text_align, psz_utf16_str, i_text_len,
833 i_runs, pi_run_lengths, pp_styles,
834 i_width, i_height, &i_textblock_height );
838 msg_Err( p_filter, "No offscreen buffer" );
842 uint8_t *p_dst_y,*p_dst_u,*p_dst_v,*p_dst_a;
844 int x, y, i_offset, i_pitch;
845 uint8_t i_y, i_u, i_v; // YUV values, derived from incoming RGB
847 // Create a new subpicture region
848 memset( &fmt, 0, sizeof(video_format_t) );
849 fmt.i_chroma = VLC_FOURCC('Y','U','V','A');
851 fmt.i_width = fmt.i_visible_width = i_width;
852 fmt.i_height = fmt.i_visible_height = i_textblock_height + VERTICAL_MARGIN * 2;
853 fmt.i_x_offset = fmt.i_y_offset = 0;
855 p_region->p_picture = picture_New( fmt.i_chroma, fmt.i_width, fmt.i_height, fmt.i_aspect );
856 if( !p_region->p_picture )
860 p_dst_y = p_region->p_picture->Y_PIXELS;
861 p_dst_u = p_region->p_picture->U_PIXELS;
862 p_dst_v = p_region->p_picture->V_PIXELS;
863 p_dst_a = p_region->p_picture->A_PIXELS;
864 i_pitch = p_region->p_picture->A_PITCH;
866 i_offset = VERTICAL_MARGIN *i_pitch;
867 for( y=0; y<i_textblock_height; y++)
869 for( x=0; x<i_width; x++)
871 int i_alpha = p_offScreen->p_data[ y * p_offScreen->i_bytesPerRow + x * p_offScreen->i_bytesPerPixel ];
872 int i_red = p_offScreen->p_data[ y * p_offScreen->i_bytesPerRow + x * p_offScreen->i_bytesPerPixel + 1 ];
873 int i_green = p_offScreen->p_data[ y * p_offScreen->i_bytesPerRow + x * p_offScreen->i_bytesPerPixel + 2 ];
874 int i_blue = p_offScreen->p_data[ y * p_offScreen->i_bytesPerRow + x * p_offScreen->i_bytesPerPixel + 3 ];
876 i_y = (uint8_t)__MIN(abs( 2104 * i_red + 4130 * i_green +
877 802 * i_blue + 4096 + 131072 ) >> 13, 235);
878 i_u = (uint8_t)__MIN(abs( -1214 * i_red + -2384 * i_green +
879 3598 * i_blue + 4096 + 1048576) >> 13, 240);
880 i_v = (uint8_t)__MIN(abs( 3598 * i_red + -3013 * i_green +
881 -585 * i_blue + 4096 + 1048576) >> 13, 240);
883 p_dst_y[ i_offset + x ] = i_y;
884 p_dst_u[ i_offset + x ] = i_u;
885 p_dst_v[ i_offset + x ] = i_v;
886 p_dst_a[ i_offset + x ] = i_alpha;
891 free( p_offScreen->p_data );