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_DEFAULT_COLOR p_sys->i_font_color
160 #define TR_FONT_STYLE_PTR ATSUStyle
162 #include "text_renderer.h"
164 //////////////////////////////////////////////////////////////////////////////
165 // Create: allocates osd-text video thread output method
166 //////////////////////////////////////////////////////////////////////////////
167 // This function allocates and initializes a Clone vout method.
168 //////////////////////////////////////////////////////////////////////////////
169 static int Create( vlc_object_t *p_this )
171 filter_t *p_filter = (filter_t *)p_this;
174 // Allocate structure
175 p_filter->p_sys = p_sys = malloc( sizeof( filter_sys_t ) );
178 p_sys->psz_font_name = var_CreateGetString( p_this, "quartztext-font" );
179 p_sys->i_font_opacity = 255;
180 p_sys->i_font_color = __MAX( __MIN( var_CreateGetInteger( p_this, "quartztext-color" ) , 0xFFFFFF ), 0 );
181 p_sys->i_font_size = GetFontSize( p_filter );
183 p_filter->pf_render_text = RenderText;
184 p_filter->pf_render_html = RenderHtml;
186 p_sys->p_fonts = NULL;
189 LoadFontsFromAttachments( p_filter );
194 //////////////////////////////////////////////////////////////////////////////
195 // Destroy: destroy Clone video thread output method
196 //////////////////////////////////////////////////////////////////////////////
197 // Clean up all data and library connections
198 //////////////////////////////////////////////////////////////////////////////
199 static void Destroy( vlc_object_t *p_this )
201 filter_t *p_filter = (filter_t *)p_this;
202 filter_sys_t *p_sys = p_filter->p_sys;
208 for( k = 0; k < p_sys->i_fonts; k++ )
210 ATSFontDeactivate( p_sys->p_fonts[k], NULL, kATSOptionFlagsDefault );
213 free( p_sys->p_fonts );
216 free( p_sys->psz_font_name );
220 //////////////////////////////////////////////////////////////////////////////
221 // Make any TTF/OTF fonts present in the attachments of the media file
222 // available to the Quartz engine for text rendering
223 //////////////////////////////////////////////////////////////////////////////
224 static int LoadFontsFromAttachments( filter_t *p_filter )
226 filter_sys_t *p_sys = p_filter->p_sys;
227 input_thread_t *p_input;
228 input_attachment_t **pp_attachments;
229 int i_attachments_cnt;
231 int rv = VLC_SUCCESS;
233 p_input = (input_thread_t *)vlc_object_find( p_filter, VLC_OBJECT_INPUT, FIND_PARENT );
237 if( VLC_SUCCESS != input_Control( p_input, INPUT_GET_ATTACHMENTS, &pp_attachments, &i_attachments_cnt ))
239 vlc_object_release(p_input);
244 p_sys->p_fonts = malloc( i_attachments_cnt * sizeof( ATSFontContainerRef ) );
245 if(! p_sys->p_fonts )
248 for( k = 0; k < i_attachments_cnt; k++ )
250 input_attachment_t *p_attach = pp_attachments[k];
254 if(( !strcmp( p_attach->psz_mime, "application/x-truetype-font" ) || // TTF
255 !strcmp( p_attach->psz_mime, "application/x-font-otf" ) ) && // OTF
256 ( p_attach->i_data > 0 ) &&
257 ( p_attach->p_data != NULL ) )
259 ATSFontContainerRef container;
261 if( noErr == ATSFontActivateFromMemory( p_attach->p_data,
263 kATSFontContextLocal,
264 kATSFontFormatUnspecified,
266 kATSOptionFlagsDefault,
269 p_sys->p_fonts[ p_sys->i_fonts++ ] = container;
273 vlc_input_attachment_Delete( p_attach );
275 free( pp_attachments );
277 vlc_object_release(p_input);
282 #if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_4
283 // Original version of these functions available on:
284 // http://developer.apple.com/documentation/Carbon/Conceptual/QuickDrawToQuartz2D/tq_color/chapter_4_section_3.html
286 #define kGenericRGBProfilePathStr "/System/Library/ColorSync/Profiles/Generic RGB Profile.icc"
288 static CMProfileRef OpenGenericProfile( void )
290 static CMProfileRef cached_rgb_prof = NULL;
292 // Create the profile reference only once
293 if( cached_rgb_prof == NULL )
296 CMProfileLocation loc;
298 loc.locType = cmPathBasedProfile;
299 strcpy( loc.u.pathLoc.path, kGenericRGBProfilePathStr );
301 err = CMOpenProfile( &cached_rgb_prof, &loc );
305 cached_rgb_prof = NULL;
309 if( cached_rgb_prof )
311 // Clone the profile reference so that the caller has
312 // their own reference, not our cached one.
313 CMCloneProfileRef( cached_rgb_prof );
316 return cached_rgb_prof;
319 static CGColorSpaceRef CreateGenericRGBColorSpace( void )
321 static CGColorSpaceRef p_generic_rgb_cs = NULL;
323 if( p_generic_rgb_cs == NULL )
325 CMProfileRef generic_rgb_prof = OpenGenericProfile();
327 if( generic_rgb_prof )
329 p_generic_rgb_cs = CGColorSpaceCreateWithPlatformColorSpace( generic_rgb_prof );
331 CMCloseProfile( generic_rgb_prof );
335 return p_generic_rgb_cs;
339 static char *EliminateCRLF( char *psz_string )
344 for( p = psz_string; p && *p; p++ )
346 if( ( *p == '\r' ) && ( *(p+1) == '\n' ) )
348 for( q = p + 1; *q; q++ )
357 // Convert UTF-8 string to UTF-16 character array -- internal Mac Endian-ness ;
358 // we don't need to worry about bidirectional text conversion as ATSUI should
359 // handle that for us automatically
360 static void ConvertToUTF16( const char *psz_utf8_str, uint32_t *pi_strlen, UniChar **ppsz_utf16_str )
362 CFStringRef p_cfString;
365 p_cfString = CFStringCreateWithCString( NULL, psz_utf8_str, kCFStringEncodingUTF8 );
369 i_string_length = CFStringGetLength( p_cfString );
372 *pi_strlen = i_string_length;
374 if( !*ppsz_utf16_str )
375 *ppsz_utf16_str = (UniChar *) calloc( i_string_length, sizeof( UniChar ) );
377 CFStringGetCharacters( p_cfString, CFRangeMake( 0, i_string_length ), *ppsz_utf16_str );
379 CFRelease( p_cfString );
382 // Renders a text subpicture region into another one.
383 // It is used as pf_add_string callback in the vout method by this module
384 static int RenderText( filter_t *p_filter, subpicture_region_t *p_region_out,
385 subpicture_region_t *p_region_in )
387 filter_sys_t *p_sys = p_filter->p_sys;
388 UniChar *psz_utf16_str = NULL;
389 uint32_t i_string_length;
391 int i_font_color, i_font_alpha, i_font_size;
395 p_sys->i_font_size = GetFontSize( p_filter );
398 if( !p_region_in || !p_region_out ) return VLC_EGENERIC;
399 psz_string = p_region_in->psz_text;
400 if( !psz_string || !*psz_string ) return VLC_EGENERIC;
402 if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
405 if( p_region_in->p_style )
407 i_font_color = __MAX( __MIN( p_region_in->p_style->i_font_color, 0xFFFFFF ), 0 );
408 i_font_alpha = __MAX( __MIN( p_region_in->p_style->i_font_alpha, 255 ), 0 );
409 i_font_size = __MAX( __MIN( p_region_in->p_style->i_font_size, 255 ), 0 ) * i_scale / 1000;
413 i_font_color = p_sys->i_font_color;
414 i_font_alpha = 255 - p_sys->i_font_opacity;
415 i_font_size = p_sys->i_font_size;
418 if( !i_font_alpha ) i_font_alpha = 255 - p_sys->i_font_opacity;
420 ConvertToUTF16( EliminateCRLF( psz_string ), &i_string_length, &psz_utf16_str );
422 p_region_out->i_x = p_region_in->i_x;
423 p_region_out->i_y = p_region_in->i_y;
425 if( psz_utf16_str != NULL )
427 ATSUStyle p_style = CreateStyle( p_sys->psz_font_name, i_font_size,
428 (i_font_color & 0xffffff) |
429 ((i_font_alpha & 0xff) << 24),
430 false, false, false );
433 RenderYUVA( p_filter, p_region_out, psz_utf16_str, i_string_length,
434 1, &i_string_length, &p_style );
437 ATSUDisposeStyle( p_style );
438 free( psz_utf16_str );
445 static ATSUStyle CreateStyle( char *psz_fontname, int i_font_size, uint32_t i_font_color,
446 bool b_bold, bool b_italic, bool b_uline )
452 float f_red = (float)(( i_font_color & 0x00FF0000 ) >> 16) / 255.0;
453 float f_green = (float)(( i_font_color & 0x0000FF00 ) >> 8) / 255.0;
454 float f_blue = (float)( i_font_color & 0x000000FF ) / 255.0;
455 float f_alpha = ( 255.0 - (float)(( i_font_color & 0xFF000000 ) >> 24)) / 255.0;
458 Fixed font_size = IntToFixed( i_font_size );
459 ATSURGBAlphaColor font_color = { f_red, f_green, f_blue, f_alpha };
460 Boolean bold = b_bold;
461 Boolean italic = b_italic;
462 Boolean uline = b_uline;
464 ATSUAttributeTag tags[] = { kATSUSizeTag, kATSURGBAlphaColorTag, kATSUQDItalicTag,
465 kATSUQDBoldfaceTag, kATSUQDUnderlineTag, kATSUFontTag };
466 ByteCount sizes[] = { sizeof( Fixed ), sizeof( ATSURGBAlphaColor ), sizeof( Boolean ),
467 sizeof( Boolean ), sizeof( Boolean ), sizeof( ATSUFontID )};
468 ATSUAttributeValuePtr values[] = { &font_size, &font_color, &italic, &bold, &uline, &font };
470 i_tag_cnt = sizeof( tags ) / sizeof( ATSUAttributeTag );
472 status = ATSUFindFontFromName( psz_fontname,
473 strlen( psz_fontname ),
480 if( status != noErr )
482 // If we can't find a suitable font, just do everything else
486 if( noErr == ATSUCreateStyle( &p_style ) )
488 if( noErr == ATSUSetAttributes( p_style, i_tag_cnt, tags, sizes, values ) )
492 ATSUDisposeStyle( p_style );
497 static ATSUStyle GetStyleFromFontStack( filter_sys_t *p_sys,
498 font_stack_t **p_fonts, bool b_bold, bool b_italic,
501 ATSUStyle p_style = NULL;
503 char *psz_fontname = NULL;
504 uint32_t i_font_color = p_sys->i_font_color;
505 uint32_t i_karaoke_bg_color = i_font_color; /* Use it */
506 int i_font_size = p_sys->i_font_size;
508 if( VLC_SUCCESS == PeekFont( p_fonts, &psz_fontname, &i_font_size,
509 &i_font_color, &i_karaoke_bg_color ))
511 p_style = CreateStyle( psz_fontname, i_font_size, i_font_color,
512 b_bold, b_italic, b_uline );
517 static void SetupLine( filter_t *p_filter, const char *psz_text_in,
518 UniChar **psz_text_out, uint32_t *pi_runs,
519 uint32_t **ppi_run_lengths, ATSUStyle **ppp_styles,
522 uint32_t i_string_length = 0;
524 ConvertToUTF16( psz_text_in, &i_string_length, psz_text_out );
525 *psz_text_out += i_string_length;
527 if( ppp_styles && ppi_run_lengths )
532 *ppp_styles = (ATSUStyle *) realloc( *ppp_styles, *pi_runs * sizeof( ATSUStyle ) );
534 *ppp_styles = (ATSUStyle *) malloc( *pi_runs * sizeof( ATSUStyle ) );
536 (*ppp_styles)[ *pi_runs - 1 ] = p_style;
538 if( *ppi_run_lengths )
539 *ppi_run_lengths = (uint32_t *) realloc( *ppi_run_lengths, *pi_runs * sizeof( uint32_t ) );
541 *ppi_run_lengths = (uint32_t *) malloc( *pi_runs * sizeof( uint32_t ) );
543 (*ppi_run_lengths)[ *pi_runs - 1 ] = i_string_length;
547 static int RenderHtml( filter_t *p_filter, subpicture_region_t *p_region_out,
548 subpicture_region_t *p_region_in )
550 int rv = VLC_SUCCESS;
551 stream_t *p_sub = NULL;
553 xml_reader_t *p_xml_reader = NULL;
555 if( !p_region_in || !p_region_in->psz_html )
558 /* Reset the default fontsize in case screen metrics have changed */
559 p_filter->p_sys->i_font_size = GetFontSize( p_filter );
561 p_sub = stream_MemoryNew( VLC_OBJECT(p_filter),
562 (uint8_t *) p_region_in->psz_html,
563 strlen( p_region_in->psz_html ),
567 p_xml = xml_Create( p_filter );
570 bool b_karaoke = false;
572 p_xml_reader = xml_ReaderCreate( p_xml, p_sub );
575 /* Look for Root Node */
576 if( xml_ReaderRead( p_xml_reader ) == 1 )
578 char *psz_node = xml_ReaderName( p_xml_reader );
580 if( !strcasecmp( "karaoke", psz_node ) )
582 /* We're going to have to render the text a number
583 * of times to show the progress marker on the text.
585 var_SetBool( p_filter, "text-rerender", true );
588 else if( !strcasecmp( "text", psz_node ) )
594 /* Only text and karaoke tags are supported */
595 xml_ReaderDelete( p_xml, p_xml_reader );
609 uint32_t i_k_runs = 0;
610 uint32_t *pi_run_lengths = NULL;
611 uint32_t *pi_k_run_lengths = NULL;
612 uint32_t *pi_k_durations = NULL;
613 ATSUStyle *pp_styles = NULL;
615 psz_text = (UniChar *) malloc( strlen( p_region_in->psz_html ) *
621 rv = ProcessNodes( p_filter, p_xml_reader,
622 p_region_in->p_style, psz_text, &i_len,
623 &i_runs, &pi_run_lengths, &pp_styles,
624 /* No karaoke support */
625 false, &i_k_runs, &pi_k_run_lengths, &pi_k_durations );
627 assert( pi_k_run_lengths == NULL && pi_k_durations == NULL );
629 p_region_out->i_x = p_region_in->i_x;
630 p_region_out->i_y = p_region_in->i_y;
632 if(( rv == VLC_SUCCESS ) && ( i_len > 0 ))
634 RenderYUVA( p_filter, p_region_out, psz_text, i_len, i_runs,
635 pi_run_lengths, pp_styles);
638 for( k=0; k<i_runs; k++)
639 ATSUDisposeStyle( pp_styles[k] );
641 free( pi_run_lengths );
645 xml_ReaderDelete( p_xml, p_xml_reader );
649 stream_Delete( p_sub );
655 static CGContextRef CreateOffScreenContext( int i_width, int i_height,
656 offscreen_bitmap_t **pp_memory, CGColorSpaceRef *pp_colorSpace )
658 offscreen_bitmap_t *p_bitmap;
659 CGContextRef p_context = NULL;
661 p_bitmap = (offscreen_bitmap_t *) malloc( sizeof( offscreen_bitmap_t ));
664 p_bitmap->i_bitsPerChannel = 8;
665 p_bitmap->i_bitsPerPixel = 4 * p_bitmap->i_bitsPerChannel; // A,R,G,B
666 p_bitmap->i_bytesPerPixel = p_bitmap->i_bitsPerPixel / 8;
667 p_bitmap->i_bytesPerRow = i_width * p_bitmap->i_bytesPerPixel;
669 p_bitmap->p_data = calloc( i_height, p_bitmap->i_bytesPerRow );
671 #if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_4
672 *pp_colorSpace = CGColorSpaceCreateWithName( kCGColorSpaceGenericRGB );
674 *pp_colorSpace = CreateGenericRGBColorSpace();
677 if( p_bitmap->p_data && *pp_colorSpace )
679 p_context = CGBitmapContextCreate( p_bitmap->p_data, i_width, i_height,
680 p_bitmap->i_bitsPerChannel, p_bitmap->i_bytesPerRow,
681 *pp_colorSpace, kCGImageAlphaPremultipliedFirst);
685 #if MAC_OS_X_VERSION_MIN_REQUIRED > MAC_OS_X_VERSION_10_1
686 // OS X 10.1 doesn't support weak linking of this call which is only available
687 // int 10.4 and later
688 if( CGContextSetAllowsAntialiasing != NULL )
690 CGContextSetAllowsAntialiasing( p_context, true );
694 *pp_memory = p_bitmap;
700 static offscreen_bitmap_t *Compose( int i_text_align, UniChar *psz_utf16_str, uint32_t i_text_len,
701 uint32_t i_runs, uint32_t *pi_run_lengths, ATSUStyle *pp_styles,
702 int i_width, int i_height, int *pi_textblock_height )
704 offscreen_bitmap_t *p_offScreen = NULL;
705 CGColorSpaceRef p_colorSpace = NULL;
706 CGContextRef p_context = NULL;
708 p_context = CreateOffScreenContext( i_width, i_height, &p_offScreen, &p_colorSpace );
712 ATSUTextLayout p_textLayout;
713 OSStatus status = noErr;
715 status = ATSUCreateTextLayoutWithTextPtr( psz_utf16_str, 0, i_text_len, i_text_len,
717 (const UniCharCount *) pi_run_lengths,
720 if( status == noErr )
722 // Attach our offscreen Image Graphics Context to the text style
723 // and setup the line alignment (have to specify the line width
724 // also in order for our chosen alignment to work)
726 Fract alignment = kATSUStartAlignment;
727 Fixed line_width = Long2Fix( i_width - HORIZONTAL_MARGIN * 2 );
729 ATSUAttributeTag tags[] = { kATSUCGContextTag, kATSULineFlushFactorTag, kATSULineWidthTag };
730 ByteCount sizes[] = { sizeof( CGContextRef ), sizeof( Fract ), sizeof( Fixed ) };
731 ATSUAttributeValuePtr values[] = { &p_context, &alignment, &line_width };
733 int i_tag_cnt = sizeof( tags ) / sizeof( ATSUAttributeTag );
735 if( i_text_align == SUBPICTURE_ALIGN_RIGHT )
737 alignment = kATSUEndAlignment;
739 else if( i_text_align != SUBPICTURE_ALIGN_LEFT )
741 alignment = kATSUCenterAlignment;
744 ATSUSetLayoutControls( p_textLayout, i_tag_cnt, tags, sizes, values );
746 // let ATSUI deal with characters not-in-our-specified-font
747 ATSUSetTransientFontMatching( p_textLayout, true );
749 Fixed x = Long2Fix( HORIZONTAL_MARGIN );
750 Fixed y = Long2Fix( i_height );
752 // Set the line-breaks and draw individual lines
753 uint32_t i_start = 0;
754 uint32_t i_end = i_text_len;
756 // Set up black outlining of the text --
757 CGContextSetRGBStrokeColor( p_context, 0, 0, 0, 0.5 );
758 CGContextSetTextDrawingMode( p_context, kCGTextFillStroke );
759 CGContextSetShadow( p_context, CGSizeMake( 0, 0 ), 5 );
760 float black_components[4] = {0, 0, 0, 1};
761 CGContextSetShadowWithColor (p_context, CGSizeMake( 0, 0 ), 5, CGColorCreate( CGColorSpaceCreateWithName( kCGColorSpaceGenericRGB ), black_components ));
764 // ATSUBreakLine will automatically pick up any manual '\n's also
765 status = ATSUBreakLine( p_textLayout, i_start, line_width, true, (UniCharArrayOffset *) &i_end );
766 if( ( status == noErr ) || ( status == kATSULineBreakInWord ) )
770 uint32_t i_actualSize;
772 // Come down far enough to fit the height of this line --
773 ATSUGetLineControl( p_textLayout, i_start, kATSULineAscentTag,
774 sizeof( Fixed ), &ascent, (ByteCount *) &i_actualSize );
776 // Quartz uses an upside-down co-ordinate space -> y values decrease as
777 // you move down the page
780 // Set the outlining for this line to be dependent on the size of the line -
781 // make it about 5% of the ascent, with a minimum at 1.0
782 float f_thickness = FixedToFloat( ascent ) * 0.05;
783 CGContextSetLineWidth( p_context, (( f_thickness < 1.0 ) ? 1.0 : f_thickness ));
784 ATSUDrawText( p_textLayout, i_start, i_end - i_start, x, y );
786 // and now prepare for the next line by coming down far enough for our
788 ATSUGetLineControl( p_textLayout, i_start, kATSULineDescentTag,
789 sizeof( Fixed ), &descent, (ByteCount *) &i_actualSize );
797 while( i_end < i_text_len );
799 *pi_textblock_height = i_height - Fix2Long( y );
800 CGContextFlush( p_context );
802 ATSUDisposeTextLayout( p_textLayout );
805 CGContextRelease( p_context );
807 if( p_colorSpace ) CGColorSpaceRelease( p_colorSpace );
812 static int GetFontSize( filter_t *p_filter )
814 return p_filter->fmt_out.video.i_height / __MAX(1, var_CreateGetInteger( p_filter, "quartztext-rel-fontsize" ));
817 static int RenderYUVA( filter_t *p_filter, subpicture_region_t *p_region, UniChar *psz_utf16_str,
818 uint32_t i_text_len, uint32_t i_runs, uint32_t *pi_run_lengths, ATSUStyle *pp_styles )
820 offscreen_bitmap_t *p_offScreen = NULL;
821 int i_textblock_height = 0;
823 int i_width = p_filter->fmt_out.video.i_visible_width;
824 int i_height = p_filter->fmt_out.video.i_visible_height;
825 int i_text_align = p_region->i_align & 0x3;
829 msg_Err( p_filter, "Invalid argument to RenderYUVA" );
833 p_offScreen = Compose( i_text_align, psz_utf16_str, i_text_len,
834 i_runs, pi_run_lengths, pp_styles,
835 i_width, i_height, &i_textblock_height );
839 msg_Err( p_filter, "No offscreen buffer" );
843 uint8_t *p_dst_y,*p_dst_u,*p_dst_v,*p_dst_a;
845 int x, y, i_offset, i_pitch;
846 uint8_t i_y, i_u, i_v; // YUV values, derived from incoming RGB
848 // Create a new subpicture region
849 memset( &fmt, 0, sizeof(video_format_t) );
850 fmt.i_chroma = VLC_FOURCC('Y','U','V','A');
852 fmt.i_width = fmt.i_visible_width = i_width;
853 fmt.i_height = fmt.i_visible_height = i_textblock_height + VERTICAL_MARGIN * 2;
854 fmt.i_x_offset = fmt.i_y_offset = 0;
856 p_region->p_picture = picture_New( fmt.i_chroma, fmt.i_width, fmt.i_height, fmt.i_aspect );
857 if( !p_region->p_picture )
861 p_dst_y = p_region->p_picture->Y_PIXELS;
862 p_dst_u = p_region->p_picture->U_PIXELS;
863 p_dst_v = p_region->p_picture->V_PIXELS;
864 p_dst_a = p_region->p_picture->A_PIXELS;
865 i_pitch = p_region->p_picture->A_PITCH;
867 i_offset = VERTICAL_MARGIN *i_pitch;
868 for( y=0; y<i_textblock_height; y++)
870 for( x=0; x<i_width; x++)
872 int i_alpha = p_offScreen->p_data[ y * p_offScreen->i_bytesPerRow + x * p_offScreen->i_bytesPerPixel ];
873 int i_red = p_offScreen->p_data[ y * p_offScreen->i_bytesPerRow + x * p_offScreen->i_bytesPerPixel + 1 ];
874 int i_green = p_offScreen->p_data[ y * p_offScreen->i_bytesPerRow + x * p_offScreen->i_bytesPerPixel + 2 ];
875 int i_blue = p_offScreen->p_data[ y * p_offScreen->i_bytesPerRow + x * p_offScreen->i_bytesPerPixel + 3 ];
877 i_y = (uint8_t)__MIN(abs( 2104 * i_red + 4130 * i_green +
878 802 * i_blue + 4096 + 131072 ) >> 13, 235);
879 i_u = (uint8_t)__MIN(abs( -1214 * i_red + -2384 * i_green +
880 3598 * i_blue + 4096 + 1048576) >> 13, 240);
881 i_v = (uint8_t)__MIN(abs( 3598 * i_red + -3013 * i_green +
882 -585 * i_blue + 4096 + 1048576) >> 13, 240);
884 p_dst_y[ i_offset + x ] = i_y;
885 p_dst_u[ i_offset + x ] = i_u;
886 p_dst_v[ i_offset + x ] = i_v;
887 p_dst_a[ i_offset + x ] = i_alpha;
892 free( p_offScreen->p_data );