X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=modules%2Fmisc%2Fquartztext.c;h=b1dd12ade814bb05b53cab008f14a5dd917ee8c0;hb=fdc455adc812cf9b02ae40c8bd47a1a261a32ce6;hp=27517e419c80e3a4a6bacf6859a2884c9abdf0f0;hpb=a4bb485ff77457762b2510d4d59c874eb6c37b2c;p=vlc diff --git a/modules/misc/quartztext.c b/modules/misc/quartztext.c index 27517e419c..b1dd12ade8 100644 --- a/modules/misc/quartztext.c +++ b/modules/misc/quartztext.c @@ -1,7 +1,7 @@ /***************************************************************************** * quartztext.c : Put text on the video, using Mac OS X Quartz Engine ***************************************************************************** - * Copyright (C) 2007 the VideoLAN team + * Copyright (C) 2007, 2009 the VideoLAN team * $Id$ * * Authors: Bernie Purcell @@ -31,19 +31,18 @@ #include #include -#include #include -#include -#include #include #include #include -#include +// Fix ourselves ColorSync headers that gets included in ApplicationServices. +#define DisposeCMProfileIterateUPP(a) DisposeCMProfileIterateUPP(CMProfileIterateUPP userUPP __attribute__((unused))) +#define DisposeCMMIterateUPP(a) DisposeCMMIterateUPP(CMProfileIterateUPP userUPP __attribute__((unused))) +#define __MACHINEEXCEPTIONS__ +#include -#include - -#define DEFAULT_FONT "Verdana" +#define DEFAULT_FONT "Arial Black" #define DEFAULT_FONT_COLOR 0xffffff #define DEFAULT_REL_FONT_SIZE 16 @@ -65,28 +64,25 @@ static int RenderHtml( filter_t *, subpicture_region_t *, static int GetFontSize( filter_t *p_filter ); static int RenderYUVA( filter_t *p_filter, subpicture_region_t *p_region, - UniChar *psz_utfString, uint32_t i_text_len, - uint32_t i_runs, uint32_t *pi_run_lengths, - ATSUStyle *pp_styles ); -static ATSUStyle CreateStyle( char *psz_fontname, int i_font_size, - uint32_t i_font_color, - bool b_bold, bool b_italic, - bool b_uline ); + CFMutableAttributedStringRef p_attrString ); + +static void setFontAttibutes( char *psz_fontname, int i_font_size, uint32_t i_font_color, + bool b_bold, bool b_italic, bool b_underline, + CFRange p_range, CFMutableAttributedStringRef p_attrString ); + ////////////////////////////////////////////////////////////////////////////// // Module descriptor ////////////////////////////////////////////////////////////////////////////// // The preferred way to set font style information is for it to come from the // subtitle file, and for it to be rendered with RenderHtml instead of -// RenderText. This module, unlike Freetype, doesn't provide any options to -// override the fallback font selection used when this style information is -// absent. +// RenderText. #define FONT_TEXT N_("Font") #define FONT_LONGTEXT N_("Name for the font you want to use") #define FONTSIZER_TEXT N_("Relative font size") #define FONTSIZER_LONGTEXT N_("This is the relative default size of the " \ "fonts that will be rendered on the video. If absolute font size is set, "\ - "relative size will be overriden." ) + "relative size will be overridden." ) #define COLOR_TEXT N_("Text default color") #define COLOR_LONGTEXT N_("The color of the text that will be rendered on "\ "the video. This must be an hexadecimal (like HTML colors). The first two "\ @@ -107,24 +103,24 @@ static const int pi_sizes[] = { 20, 18, 16, 12, 6 }; static const char *const ppsz_sizes_text[] = { N_("Smaller"), N_("Small"), N_("Normal"), N_("Large"), N_("Larger") }; -vlc_module_begin(); - set_shortname( N_("Mac Text renderer")); - set_description( N_("Quartz font renderer") ); - set_category( CAT_VIDEO ); - set_subcategory( SUBCAT_VIDEO_SUBPIC ); +vlc_module_begin () + set_shortname( N_("Text renderer for Mac")) + set_description( N_("CoreText font renderer") ) + set_category( CAT_VIDEO ) + set_subcategory( SUBCAT_VIDEO_SUBPIC ) add_string( "quartztext-font", DEFAULT_FONT, NULL, FONT_TEXT, FONT_LONGTEXT, - false ); + false ) add_integer( "quartztext-rel-fontsize", DEFAULT_REL_FONT_SIZE, NULL, FONTSIZER_TEXT, - FONTSIZER_LONGTEXT, false ); - change_integer_list( pi_sizes, ppsz_sizes_text, 0 ); + FONTSIZER_LONGTEXT, false ) + change_integer_list( pi_sizes, ppsz_sizes_text, NULL ); add_integer( "quartztext-color", 0x00FFFFFF, NULL, COLOR_TEXT, - COLOR_LONGTEXT, false ); - change_integer_list( pi_color_values, ppsz_color_descriptions, 0 ); - set_capability( "text renderer", 120 ); - add_shortcut( "text" ); - set_callbacks( Create, Destroy ); -vlc_module_end(); + COLOR_LONGTEXT, false ) + change_integer_list( pi_color_values, ppsz_color_descriptions, NULL ); + set_capability( "text renderer", 150 ) + add_shortcut( "text" ) + set_callbacks( Create, Destroy ) +vlc_module_end () typedef struct font_stack_t font_stack_t; struct font_stack_t @@ -136,6 +132,16 @@ struct font_stack_t font_stack_t *p_next; }; +typedef struct +{ + int i_font_size; + uint32_t i_font_color; /* ARGB */ + bool b_italic; + bool b_bold; + bool b_underline; + char *psz_fontname; +} ft_style_t; + typedef struct offscreen_bitmap_t offscreen_bitmap_t; struct offscreen_bitmap_t { @@ -226,117 +232,44 @@ static void Destroy( vlc_object_t *p_this ) static int LoadFontsFromAttachments( filter_t *p_filter ) { filter_sys_t *p_sys = p_filter->p_sys; - input_thread_t *p_input; input_attachment_t **pp_attachments; int i_attachments_cnt; - int k; - int rv = VLC_SUCCESS; - p_input = (input_thread_t *)vlc_object_find( p_filter, VLC_OBJECT_INPUT, FIND_PARENT ); - if( ! p_input ) + if( filter_GetInputAttachments( p_filter, &pp_attachments, &i_attachments_cnt ) ) return VLC_EGENERIC; - if( VLC_SUCCESS != input_Control( p_input, INPUT_GET_ATTACHMENTS, &pp_attachments, &i_attachments_cnt )) - { - vlc_object_release(p_input); - return VLC_EGENERIC; - } - p_sys->i_fonts = 0; p_sys->p_fonts = malloc( i_attachments_cnt * sizeof( ATSFontContainerRef ) ); if(! p_sys->p_fonts ) - rv = VLC_ENOMEM; + return VLC_ENOMEM; - for( k = 0; k < i_attachments_cnt; k++ ) + for( int k = 0; k < i_attachments_cnt; k++ ) { input_attachment_t *p_attach = pp_attachments[k]; - if( p_sys->p_fonts ) + if( ( !strcmp( p_attach->psz_mime, "application/x-truetype-font" ) || // TTF + !strcmp( p_attach->psz_mime, "application/x-font-otf" ) ) && // OTF + p_attach->i_data > 0 && p_attach->p_data ) { - if(( !strcmp( p_attach->psz_mime, "application/x-truetype-font" ) || // TTF - !strcmp( p_attach->psz_mime, "application/x-font-otf" ) ) && // OTF - ( p_attach->i_data > 0 ) && - ( p_attach->p_data != NULL ) ) + ATSFontContainerRef container; + + if( noErr == ATSFontActivateFromMemory( p_attach->p_data, + p_attach->i_data, + kATSFontContextLocal, + kATSFontFormatUnspecified, + NULL, + kATSOptionFlagsDefault, + &container )) { - ATSFontContainerRef container; - - if( noErr == ATSFontActivateFromMemory( p_attach->p_data, - p_attach->i_data, - kATSFontContextLocal, - kATSFontFormatUnspecified, - NULL, - kATSOptionFlagsDefault, - &container )) - { - p_sys->p_fonts[ p_sys->i_fonts++ ] = container; - } + p_sys->p_fonts[ p_sys->i_fonts++ ] = container; } } vlc_input_attachment_Delete( p_attach ); } free( pp_attachments ); - vlc_object_release(p_input); - - return rv; -} - -#if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_4 -// Original version of these functions available on: -// http://developer.apple.com/documentation/Carbon/Conceptual/QuickDrawToQuartz2D/tq_color/chapter_4_section_3.html - -#define kGenericRGBProfilePathStr "/System/Library/ColorSync/Profiles/Generic RGB Profile.icc" - -static CMProfileRef OpenGenericProfile( void ) -{ - static CMProfileRef cached_rgb_prof = NULL; - - // Create the profile reference only once - if( cached_rgb_prof == NULL ) - { - OSStatus err; - CMProfileLocation loc; - - loc.locType = cmPathBasedProfile; - strcpy( loc.u.pathLoc.path, kGenericRGBProfilePathStr ); - - err = CMOpenProfile( &cached_rgb_prof, &loc ); - - if( err != noErr ) - { - cached_rgb_prof = NULL; - } - } - - if( cached_rgb_prof ) - { - // Clone the profile reference so that the caller has - // their own reference, not our cached one. - CMCloneProfileRef( cached_rgb_prof ); - } - - return cached_rgb_prof; -} - -static CGColorSpaceRef CreateGenericRGBColorSpace( void ) -{ - static CGColorSpaceRef p_generic_rgb_cs = NULL; - - if( p_generic_rgb_cs == NULL ) - { - CMProfileRef generic_rgb_prof = OpenGenericProfile(); - - if( generic_rgb_prof ) - { - p_generic_rgb_cs = CGColorSpaceCreateWithPlatformColorSpace( generic_rgb_prof ); - - CMCloseProfile( generic_rgb_prof ); - } - } - - return p_generic_rgb_cs; + return VLC_SUCCESS; } -#endif static char *EliminateCRLF( char *psz_string ) { @@ -356,43 +289,19 @@ static char *EliminateCRLF( char *psz_string ) return psz_string; } -// Convert UTF-8 string to UTF-16 character array -- internal Mac Endian-ness ; -// we don't need to worry about bidirectional text conversion as ATSUI should -// handle that for us automatically -static void ConvertToUTF16( const char *psz_utf8_str, uint32_t *pi_strlen, UniChar **ppsz_utf16_str ) -{ - CFStringRef p_cfString; - int i_string_length; - - p_cfString = CFStringCreateWithCString( NULL, psz_utf8_str, kCFStringEncodingUTF8 ); - if( !p_cfString ) - return; - - i_string_length = CFStringGetLength( p_cfString ); - - if( pi_strlen ) - *pi_strlen = i_string_length; - - if( !*ppsz_utf16_str ) - *ppsz_utf16_str = (UniChar *) calloc( i_string_length, sizeof( UniChar ) ); - - CFStringGetCharacters( p_cfString, CFRangeMake( 0, i_string_length ), *ppsz_utf16_str ); - - CFRelease( p_cfString ); -} - // Renders a text subpicture region into another one. // It is used as pf_add_string callback in the vout method by this module static int RenderText( filter_t *p_filter, subpicture_region_t *p_region_out, subpicture_region_t *p_region_in ) { filter_sys_t *p_sys = p_filter->p_sys; - UniChar *psz_utf16_str = NULL; - uint32_t i_string_length; char *psz_string; - int i_font_color, i_font_alpha, i_font_size; + int i_font_alpha, i_font_size; + uint32_t i_font_color; + bool b_bold, b_uline, b_italic; vlc_value_t val; int i_scale = 1000; + b_bold = b_uline = b_italic = FALSE; p_sys->i_font_size = GetFontSize( p_filter ); @@ -409,6 +318,15 @@ static int RenderText( filter_t *p_filter, subpicture_region_t *p_region_out, i_font_color = __MAX( __MIN( p_region_in->p_style->i_font_color, 0xFFFFFF ), 0 ); i_font_alpha = __MAX( __MIN( p_region_in->p_style->i_font_alpha, 255 ), 0 ); i_font_size = __MAX( __MIN( p_region_in->p_style->i_font_size, 255 ), 0 ) * i_scale / 1000; + if( p_region_in->p_style->i_style_flags ) + { + if( p_region_in->p_style->i_style_flags & STYLE_BOLD ) + b_bold = TRUE; + if( p_region_in->p_style->i_style_flags & STYLE_ITALIC ) + b_italic = TRUE; + if( p_region_in->p_style->i_style_flags & STYLE_UNDERLINE ) + b_uline = TRUE; + } } else { @@ -419,83 +337,42 @@ static int RenderText( filter_t *p_filter, subpicture_region_t *p_region_out, if( !i_font_alpha ) i_font_alpha = 255 - p_sys->i_font_opacity; - ConvertToUTF16( EliminateCRLF( psz_string ), &i_string_length, &psz_utf16_str ); + if( i_font_size <= 0 ) + { + msg_Warn( p_filter, "invalid fontsize, using 12" ); + if( VLC_SUCCESS == var_Get( p_filter, "scale", &val )) + i_font_size = 12 * val.i_int / 1000; + else + i_font_size = 12; + } p_region_out->i_x = p_region_in->i_x; p_region_out->i_y = p_region_in->i_y; - if( psz_utf16_str != NULL ) - { - ATSUStyle p_style = CreateStyle( p_sys->psz_font_name, i_font_size, - (i_font_color & 0xffffff) | - ((i_font_alpha & 0xff) << 24), - false, false, false ); - if( p_style ) - { - RenderYUVA( p_filter, p_region_out, psz_utf16_str, i_string_length, - 1, &i_string_length, &p_style ); - } + CFMutableAttributedStringRef p_attrString = CFAttributedStringCreateMutable(kCFAllocatorDefault, 0); - ATSUDisposeStyle( p_style ); - free( psz_utf16_str ); - } + if( p_attrString ) + { + CFStringRef p_cfString; + int len; - return VLC_SUCCESS; -} + EliminateCRLF( psz_string); + p_cfString = CFStringCreateWithCString( NULL, psz_string, kCFStringEncodingUTF8 ); + CFAttributedStringReplaceString( p_attrString, CFRangeMake(0, 0), p_cfString ); + CFRelease( p_cfString ); + len = CFAttributedStringGetLength( p_attrString ); + setFontAttibutes( p_sys->psz_font_name, i_font_size, i_font_color, b_bold, b_italic, b_uline, + CFRangeMake( 0, len ), p_attrString); -static ATSUStyle CreateStyle( char *psz_fontname, int i_font_size, uint32_t i_font_color, - bool b_bold, bool b_italic, bool b_uline ) -{ - ATSUStyle p_style; - OSStatus status; - uint32_t i_tag_cnt; - - float f_red = (float)(( i_font_color & 0x00FF0000 ) >> 16) / 255.0; - float f_green = (float)(( i_font_color & 0x0000FF00 ) >> 8) / 255.0; - float f_blue = (float)( i_font_color & 0x000000FF ) / 255.0; - float f_alpha = ( 255.0 - (float)(( i_font_color & 0xFF000000 ) >> 24)) / 255.0; - - ATSUFontID font; - Fixed font_size = IntToFixed( i_font_size ); - ATSURGBAlphaColor font_color = { f_red, f_green, f_blue, f_alpha }; - Boolean bold = b_bold; - Boolean italic = b_italic; - Boolean uline = b_uline; - - ATSUAttributeTag tags[] = { kATSUSizeTag, kATSURGBAlphaColorTag, kATSUQDItalicTag, - kATSUQDBoldfaceTag, kATSUQDUnderlineTag, kATSUFontTag }; - ByteCount sizes[] = { sizeof( Fixed ), sizeof( ATSURGBAlphaColor ), sizeof( Boolean ), - sizeof( Boolean ), sizeof( Boolean ), sizeof( ATSUFontID )}; - ATSUAttributeValuePtr values[] = { &font_size, &font_color, &italic, &bold, &uline, &font }; - - i_tag_cnt = sizeof( tags ) / sizeof( ATSUAttributeTag ); - - status = ATSUFindFontFromName( psz_fontname, - strlen( psz_fontname ), - kFontFullName, - kFontNoPlatform, - kFontNoScript, - kFontNoLanguageCode, - &font ); - - if( status != noErr ) - { - // If we can't find a suitable font, just do everything else - i_tag_cnt--; + RenderYUVA( p_filter, p_region_out, p_attrString ); } + CFRelease(p_attrString); - if( noErr == ATSUCreateStyle( &p_style ) ) - { - if( noErr == ATSUSetAttributes( p_style, i_tag_cnt, tags, sizes, values ) ) - { - return p_style; - } - ATSUDisposeStyle( p_style ); - } - return NULL; + return VLC_SUCCESS; } + static int PushFont( font_stack_t **p_font, const char *psz_name, int i_size, uint32_t i_color ) { @@ -582,25 +459,6 @@ static int PeekFont( font_stack_t **p_font, char **psz_name, int *i_size, return VLC_SUCCESS; } -static ATSUStyle GetStyleFromFontStack( filter_sys_t *p_sys, - font_stack_t **p_fonts, bool b_bold, bool b_italic, - bool b_uline ) -{ - ATSUStyle p_style = NULL; - - char *psz_fontname = NULL; - uint32_t i_font_color = p_sys->i_font_color; - int i_font_size = p_sys->i_font_size; - - if( VLC_SUCCESS == PeekFont( p_fonts, &psz_fontname, &i_font_size, - &i_font_color )) - { - p_style = CreateStyle( psz_fontname, i_font_size, i_font_color, - b_bold, b_italic, b_uline ); - } - return p_style; -} - static int HandleFontAttributes( xml_reader_t *p_xml_reader, font_stack_t **p_fonts, int i_scale ) { @@ -677,19 +535,113 @@ static int HandleFontAttributes( xml_reader_t *p_xml_reader, return rv; } +static void setFontAttibutes( char *psz_fontname, int i_font_size, uint32_t i_font_color, + bool b_bold, bool b_italic, bool b_underline, + CFRange p_range, CFMutableAttributedStringRef p_attrString ) +{ + CFStringRef p_cfString; + CTFontRef p_font; + + // Handle font name and size + p_cfString = CFStringCreateWithCString( NULL, + psz_fontname, + kCFStringEncodingUTF8 ); + p_font = CTFontCreateWithName( p_cfString, + (float)i_font_size, + NULL ); + CFRelease( p_cfString ); + CFAttributedStringSetAttribute( p_attrString, + p_range, + kCTFontAttributeName, + p_font ); + CFRelease( p_font ); + + // Handle Underline + SInt32 _uline; + if( b_underline ) + _uline = kCTUnderlineStyleSingle; + else + _uline = kCTUnderlineStyleNone; + + CFNumberRef underline = CFNumberCreate(NULL, kCFNumberSInt32Type, &_uline); + CFAttributedStringSetAttribute( p_attrString, + p_range, + kCTUnderlineStyleAttributeName, + underline ); + CFRelease( underline ); + + // Handle Bold + float _weight; + if( b_bold ) + _weight = 0.5; + else + _weight = 0.0; + + CFNumberRef weight = CFNumberCreate(NULL, kCFNumberFloatType, &_weight); + CFAttributedStringSetAttribute( p_attrString, + p_range, + kCTFontWeightTrait, + weight ); + CFRelease( weight ); + + // Handle Italic + float _slant; + if( b_italic ) + _slant = 1.0; + else + _slant = 0.0; + + CFNumberRef slant = CFNumberCreate(NULL, kCFNumberFloatType, &_slant); + CFAttributedStringSetAttribute( p_attrString, + p_range, + kCTFontSlantTrait, + slant ); + CFRelease( slant ); + + // Handle foreground colour + CGColorSpaceRef rgbColorSpace = CGColorSpaceCreateDeviceRGB(); + CGFloat components[] = { (float)((i_font_color & 0x00ff0000) >> 16) / 255.0, + (float)((i_font_color & 0x0000ff00) >> 8) / 255.0, + (float)((i_font_color & 0x000000ff) ) / 255.0, + (float)(255-((i_font_color & 0xff000000) >> 24)) / 255.0 }; + CGColorRef fg_text = CGColorCreate(rgbColorSpace, components); + CGColorSpaceRelease(rgbColorSpace); + + CFAttributedStringSetAttribute( p_attrString, + p_range, + kCTForegroundColorAttributeName, + fg_text ); + CFRelease( fg_text ); + +} + +static void GetAttrStrFromFontStack( font_stack_t **p_fonts, + bool b_bold, bool b_italic, bool b_uline, + CFRange p_range, CFMutableAttributedStringRef p_attrString ) +{ + char *psz_fontname = NULL; + int i_font_size = 0; + uint32_t i_font_color = 0; + + if( VLC_SUCCESS == PeekFont( p_fonts, &psz_fontname, &i_font_size, + &i_font_color )) + { + setFontAttibutes( psz_fontname, + i_font_size, + i_font_color, + b_bold, b_italic, b_uline, + p_range, + p_attrString ); + } +} + static int ProcessNodes( filter_t *p_filter, xml_reader_t *p_xml_reader, text_style_t *p_font_style, - UniChar *psz_text, - int *pi_len, - - uint32_t *pi_runs, - uint32_t **ppi_run_lengths, - ATSUStyle **ppp_styles ) + CFMutableAttributedStringRef p_attrString ) { int rv = VLC_SUCCESS; filter_sys_t *p_sys = p_filter->p_sys; - UniChar *psz_text_orig = psz_text; font_stack_t *p_fonts = NULL; vlc_value_t val; int i_scale = 1000; @@ -765,26 +717,16 @@ static int ProcessNodes( filter_t *p_filter, b_uline = true; else if( !strcasecmp( "br", psz_node ) ) { - uint32_t i_string_length; - - ConvertToUTF16( "\n", &i_string_length, &psz_text ); - psz_text += i_string_length; - - (*pi_runs)++; - - if( *ppp_styles ) - *ppp_styles = (ATSUStyle *) realloc( *ppp_styles, *pi_runs * sizeof( ATSUStyle ) ); - else - *ppp_styles = (ATSUStyle *) malloc( *pi_runs * sizeof( ATSUStyle ) ); - - (*ppp_styles)[ *pi_runs - 1 ] = GetStyleFromFontStack( p_sys, &p_fonts, b_bold, b_italic, b_uline ); - - if( *ppi_run_lengths ) - *ppi_run_lengths = (uint32_t *) realloc( *ppi_run_lengths, *pi_runs * sizeof( uint32_t ) ); - else - *ppi_run_lengths = (uint32_t *) malloc( *pi_runs * sizeof( uint32_t ) ); - - (*ppi_run_lengths)[ *pi_runs - 1 ] = i_string_length; + CFMutableAttributedStringRef p_attrnode = CFAttributedStringCreateMutable(kCFAllocatorDefault, 0); + CFAttributedStringReplaceString( p_attrnode, CFRangeMake(0, 0), CFSTR("\n") ); + + GetAttrStrFromFontStack( &p_fonts, b_bold, b_italic, b_uline, + CFRangeMake( 0, 1 ), + p_attrnode ); + CFAttributedStringReplaceAttributedString( p_attrString, + CFRangeMake(CFAttributedStringGetLength(p_attrString), 0), + p_attrnode); + CFRelease( p_attrnode ); } free( psz_node ); } @@ -793,7 +735,8 @@ static int ProcessNodes( filter_t *p_filter, psz_node = xml_ReaderValue( p_xml_reader ); if( psz_node ) { - uint32_t i_string_length; + CFStringRef p_cfString; + int len; // Turn any multiple-whitespaces into single spaces char *s = strpbrk( psz_node, "\t\r\n " ); @@ -810,33 +753,27 @@ static int ProcessNodes( filter_t *p_filter, s = strpbrk( s, "\t\r\n " ); } - ConvertToUTF16( psz_node, &i_string_length, &psz_text ); - psz_text += i_string_length; - (*pi_runs)++; + CFMutableAttributedStringRef p_attrnode = CFAttributedStringCreateMutable(kCFAllocatorDefault, 0); + p_cfString = CFStringCreateWithCString( NULL, psz_node, kCFStringEncodingUTF8 ); + CFAttributedStringReplaceString( p_attrnode, CFRangeMake(0, 0), p_cfString ); + CFRelease( p_cfString ); + len = CFAttributedStringGetLength( p_attrnode ); - if( *ppp_styles ) - *ppp_styles = (ATSUStyle *) realloc( *ppp_styles, *pi_runs * sizeof( ATSUStyle ) ); - else - *ppp_styles = (ATSUStyle *) malloc( *pi_runs * sizeof( ATSUStyle ) ); - - (*ppp_styles)[ *pi_runs - 1 ] = GetStyleFromFontStack( p_sys, &p_fonts, b_bold, b_italic, b_uline ); - - if( *ppi_run_lengths ) - *ppi_run_lengths = (uint32_t *) realloc( *ppi_run_lengths, *pi_runs * sizeof( uint32_t ) ); - else - *ppi_run_lengths = (uint32_t *) malloc( *pi_runs * sizeof( uint32_t ) ); - - (*ppi_run_lengths)[ *pi_runs - 1 ] = i_string_length; + GetAttrStrFromFontStack( &p_fonts, b_bold, b_italic, b_uline, + CFRangeMake( 0, len ), + p_attrnode ); + CFAttributedStringReplaceAttributedString( p_attrString, + CFRangeMake(CFAttributedStringGetLength(p_attrString), 0), + p_attrnode); + CFRelease( p_attrnode ); free( psz_node ); } break; } } - *pi_len = psz_text - psz_text_orig; - while( VLC_SUCCESS == PopFont( &p_fonts ) ); return rv; @@ -890,7 +827,7 @@ static int RenderHtml( filter_t *p_filter, subpicture_region_t *p_region_out, else { /* Only text and karaoke tags are supported */ - xml_ReaderDelete( p_xml, p_xml_reader ); + xml_ReaderDelete( p_xml_reader ); p_xml_reader = NULL; rv = VLC_EGENERIC; } @@ -901,39 +838,24 @@ static int RenderHtml( filter_t *p_filter, subpicture_region_t *p_region_out, if( p_xml_reader ) { - UniChar *psz_text; - int i_len = 0; - uint32_t i_runs = 0; - uint32_t *pi_run_lengths = NULL; - ATSUStyle *pp_styles = NULL; - - psz_text = (UniChar *) malloc( strlen( p_region_in->psz_html ) * - sizeof( UniChar ) ); - if( psz_text ) - { - uint32_t k; + int i_len; - rv = ProcessNodes( p_filter, p_xml_reader, - p_region_in->p_style, psz_text, &i_len, - &i_runs, &pi_run_lengths, &pp_styles ); + CFMutableAttributedStringRef p_attrString = CFAttributedStringCreateMutable(kCFAllocatorDefault, 0); + rv = ProcessNodes( p_filter, p_xml_reader, + p_region_in->p_style, p_attrString ); - p_region_out->i_x = p_region_in->i_x; - p_region_out->i_y = p_region_in->i_y; + i_len = CFAttributedStringGetLength( p_attrString ); - if(( rv == VLC_SUCCESS ) && ( i_len > 0 )) - { - RenderYUVA( p_filter, p_region_out, psz_text, i_len, i_runs, - pi_run_lengths, pp_styles); - } + p_region_out->i_x = p_region_in->i_x; + p_region_out->i_y = p_region_in->i_y; - for( k=0; k 0 )) + { + RenderYUVA( p_filter, p_region_out, p_attrString ); } + CFRelease(p_attrString); - xml_ReaderDelete( p_xml, p_xml_reader ); + xml_ReaderDelete( p_xml_reader ); } xml_Delete( p_xml ); } @@ -959,11 +881,7 @@ static CGContextRef CreateOffScreenContext( int i_width, int i_height, p_bitmap->p_data = calloc( i_height, p_bitmap->i_bytesPerRow ); -#if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_4 *pp_colorSpace = CGColorSpaceCreateWithName( kCGColorSpaceGenericRGB ); -#else - *pp_colorSpace = CreateGenericRGBColorSpace(); -#endif if( p_bitmap->p_data && *pp_colorSpace ) { @@ -973,14 +891,10 @@ static CGContextRef CreateOffScreenContext( int i_width, int i_height, } if( p_context ) { -#if MAC_OS_X_VERSION_MIN_REQUIRED > MAC_OS_X_VERSION_10_1 - // OS X 10.1 doesn't support weak linking of this call which is only available - // int 10.4 and later if( CGContextSetAllowsAntialiasing != NULL ) { CGContextSetAllowsAntialiasing( p_context, true ); } -#endif } *pp_memory = p_bitmap; } @@ -988,9 +902,11 @@ static CGContextRef CreateOffScreenContext( int i_width, int i_height, return p_context; } -static offscreen_bitmap_t *Compose( int i_text_align, UniChar *psz_utf16_str, uint32_t i_text_len, - uint32_t i_runs, uint32_t *pi_run_lengths, ATSUStyle *pp_styles, - int i_width, int i_height, int *pi_textblock_height ) +static offscreen_bitmap_t *Compose( int i_text_align, + CFMutableAttributedStringRef p_attrString, + unsigned i_width, + unsigned i_height, + unsigned *pi_textblock_height ) { offscreen_bitmap_t *p_offScreen = NULL; CGColorSpaceRef p_colorSpace = NULL; @@ -998,100 +914,75 @@ static offscreen_bitmap_t *Compose( int i_text_align, UniChar *psz_utf16_str, ui p_context = CreateOffScreenContext( i_width, i_height, &p_offScreen, &p_colorSpace ); + *pi_textblock_height = 0; if( p_context ) { - ATSUTextLayout p_textLayout; - OSStatus status = noErr; - - status = ATSUCreateTextLayoutWithTextPtr( psz_utf16_str, 0, i_text_len, i_text_len, - i_runs, - (const UniCharCount *) pi_run_lengths, - pp_styles, - &p_textLayout ); - if( status == noErr ) - { - // Attach our offscreen Image Graphics Context to the text style - // and setup the line alignment (have to specify the line width - // also in order for our chosen alignment to work) - - Fract alignment = kATSUStartAlignment; - Fixed line_width = Long2Fix( i_width - HORIZONTAL_MARGIN * 2 ); + float horiz_flush; - ATSUAttributeTag tags[] = { kATSUCGContextTag, kATSULineFlushFactorTag, kATSULineWidthTag }; - ByteCount sizes[] = { sizeof( CGContextRef ), sizeof( Fract ), sizeof( Fixed ) }; - ATSUAttributeValuePtr values[] = { &p_context, &alignment, &line_width }; + CGContextSetTextMatrix( p_context, CGAffineTransformIdentity ); - int i_tag_cnt = sizeof( tags ) / sizeof( ATSUAttributeTag ); + if( i_text_align == SUBPICTURE_ALIGN_RIGHT ) + horiz_flush = 1.0; + else if( i_text_align != SUBPICTURE_ALIGN_LEFT ) + horiz_flush = 0.5; + else + horiz_flush = 0.0; - if( i_text_align == SUBPICTURE_ALIGN_RIGHT ) - { - alignment = kATSUEndAlignment; - } - else if( i_text_align != SUBPICTURE_ALIGN_LEFT ) - { - alignment = kATSUCenterAlignment; - } - - ATSUSetLayoutControls( p_textLayout, i_tag_cnt, tags, sizes, values ); - - // let ATSUI deal with characters not-in-our-specified-font - ATSUSetTransientFontMatching( p_textLayout, true ); + // Create the framesetter with the attributed string. + CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString(p_attrString); + if( framesetter ) + { + CTFrameRef frame; + CGMutablePathRef p_path = CGPathCreateMutable(); + CGRect p_bounds = CGRectMake( (float)HORIZONTAL_MARGIN, + (float)VERTICAL_MARGIN, + (float)(i_width - HORIZONTAL_MARGIN*2), + (float)(i_height - VERTICAL_MARGIN *2)); + CGPathAddRect( p_path, NULL, p_bounds ); - Fixed x = Long2Fix( HORIZONTAL_MARGIN ); - Fixed y = Long2Fix( i_height ); + // Create the frame and draw it into the graphics context + frame = CTFramesetterCreateFrame(framesetter, CFRangeMake(0, 0), p_path, NULL); - // Set the line-breaks and draw individual lines - uint32_t i_start = 0; - uint32_t i_end = i_text_len; + CGPathRelease(p_path); // Set up black outlining of the text -- CGContextSetRGBStrokeColor( p_context, 0, 0, 0, 0.5 ); CGContextSetTextDrawingMode( p_context, kCGTextFillStroke ); - do + if( frame != NULL ) { - // ATSUBreakLine will automatically pick up any manual '\n's also - status = ATSUBreakLine( p_textLayout, i_start, line_width, true, (UniCharArrayOffset *) &i_end ); - if( ( status == noErr ) || ( status == kATSULineBreakInWord ) ) - { - Fixed ascent; - Fixed descent; - uint32_t i_actualSize; + CFArrayRef lines; + CGPoint penPosition; - // Come down far enough to fit the height of this line -- - ATSUGetLineControl( p_textLayout, i_start, kATSULineAscentTag, - sizeof( Fixed ), &ascent, (ByteCount *) &i_actualSize ); + lines = CTFrameGetLines( frame ); + penPosition.y = i_height; + for (int i=0; i y values decrease as - // you move down the page - y -= ascent; + CTLineRef line = (CTLineRef)CFArrayGetValueAtIndex(lines, i); + CTLineGetTypographicBounds(line, &ascent, &descent, &leading); - // Set the outlining for this line to be dependent on the size of the line - + // Set the outlining for this line to be dependant on the size of the line - // make it about 5% of the ascent, with a minimum at 1.0 - float f_thickness = FixedToFloat( ascent ) * 0.05; + float f_thickness = ascent * 0.05; CGContextSetLineWidth( p_context, (( f_thickness > 1.0 ) ? 1.0 : f_thickness )); - ATSUDrawText( p_textLayout, i_start, i_end - i_start, x, y ); + double penOffset = CTLineGetPenOffsetForFlush(line, horiz_flush, (i_width - HORIZONTAL_MARGIN*2)); + penPosition.x = HORIZONTAL_MARGIN + penOffset; + penPosition.y -= ascent; + CGContextSetTextPosition( p_context, penPosition.x, penPosition.y ); + CTLineDraw( line, p_context ); + penPosition.y -= descent + leading; - // and now prepare for the next line by coming down far enough for our - // descent - ATSUGetLineControl( p_textLayout, i_start, kATSULineDescentTag, - sizeof( Fixed ), &descent, (ByteCount *) &i_actualSize ); - y -= descent; - - i_start = i_end; } - else - break; - } - while( i_end < i_text_len ); - - *pi_textblock_height = i_height - Fix2Long( y ); - CGContextFlush( p_context ); + *pi_textblock_height = i_height - penPosition.y; - ATSUDisposeTextLayout( p_textLayout ); + CFRelease(frame); + } + CFRelease(framesetter); } - + CGContextFlush( p_context ); CGContextRelease( p_context ); } if( p_colorSpace ) CGColorSpaceRelease( p_colorSpace ); @@ -1101,27 +992,26 @@ static offscreen_bitmap_t *Compose( int i_text_align, UniChar *psz_utf16_str, ui static int GetFontSize( filter_t *p_filter ) { - return p_filter->fmt_out.video.i_height / __MAX(1, var_CreateGetInteger( p_filter, "quartztext-rel-fontsize" )); + return p_filter->fmt_out.video.i_height / DEFAULT_REL_FONT_SIZE; } -static int RenderYUVA( filter_t *p_filter, subpicture_region_t *p_region, UniChar *psz_utf16_str, - uint32_t i_text_len, uint32_t i_runs, uint32_t *pi_run_lengths, ATSUStyle *pp_styles ) +static int RenderYUVA( filter_t *p_filter, subpicture_region_t *p_region, + CFMutableAttributedStringRef p_attrString ) { offscreen_bitmap_t *p_offScreen = NULL; - int i_textblock_height = 0; + unsigned i_textblock_height = 0; - int i_width = p_filter->fmt_out.video.i_visible_width; - int i_height = p_filter->fmt_out.video.i_visible_height; - int i_text_align = p_region->i_align & 0x3; + unsigned i_width = p_filter->fmt_out.video.i_visible_width; + unsigned i_height = p_filter->fmt_out.video.i_visible_height; + unsigned i_text_align = p_region->i_align & 0x3; - if( !psz_utf16_str ) + if( !p_attrString ) { msg_Err( p_filter, "Invalid argument to RenderYUVA" ); return VLC_EGENERIC; } - p_offScreen = Compose( i_text_align, psz_utf16_str, i_text_len, - i_runs, pi_run_lengths, pp_styles, + p_offScreen = Compose( i_text_align, p_attrString, i_width, i_height, &i_textblock_height ); if( !p_offScreen ) @@ -1132,37 +1022,32 @@ static int RenderYUVA( filter_t *p_filter, subpicture_region_t *p_region, UniCha uint8_t *p_dst_y,*p_dst_u,*p_dst_v,*p_dst_a; video_format_t fmt; - int x, y, i_offset, i_pitch; + int i_offset; + unsigned x, y, i_pitch; uint8_t i_y, i_u, i_v; // YUV values, derived from incoming RGB - subpicture_region_t *p_region_tmp; // Create a new subpicture region memset( &fmt, 0, sizeof(video_format_t) ); - fmt.i_chroma = VLC_FOURCC('Y','U','V','A'); - fmt.i_aspect = 0; + fmt.i_chroma = VLC_CODEC_YUVA; fmt.i_width = fmt.i_visible_width = i_width; - fmt.i_height = fmt.i_visible_height = i_textblock_height + VERTICAL_MARGIN * 2; + fmt.i_height = fmt.i_visible_height = __MIN( i_height, i_textblock_height + VERTICAL_MARGIN * 2); fmt.i_x_offset = fmt.i_y_offset = 0; - p_region_tmp = spu_CreateRegion( p_filter, &fmt ); - if( !p_region_tmp ) - { - msg_Err( p_filter, "cannot allocate SPU region" ); + + p_region->p_picture = picture_NewFromFormat( &fmt ); + if( !p_region->p_picture ) return VLC_EGENERIC; - } - p_region->fmt = p_region_tmp->fmt; - p_region->picture = p_region_tmp->picture; - free( p_region_tmp ); - - p_dst_y = p_region->picture.Y_PIXELS; - p_dst_u = p_region->picture.U_PIXELS; - p_dst_v = p_region->picture.V_PIXELS; - p_dst_a = p_region->picture.A_PIXELS; - i_pitch = p_region->picture.A_PITCH; - - i_offset = VERTICAL_MARGIN *i_pitch; - for( y=0; yfmt = fmt; + + p_dst_y = p_region->p_picture->Y_PIXELS; + p_dst_u = p_region->p_picture->U_PIXELS; + p_dst_v = p_region->p_picture->V_PIXELS; + p_dst_a = p_region->p_picture->A_PIXELS; + i_pitch = p_region->p_picture->A_PITCH; + + i_offset = (i_height + VERTICAL_MARGIN < fmt.i_height) ? VERTICAL_MARGIN *i_pitch : 0 ; + for( y = 0; y < fmt.i_height; y++) { - for( x=0; xp_data[ y * p_offScreen->i_bytesPerRow + x * p_offScreen->i_bytesPerPixel ]; int i_red = p_offScreen->p_data[ y * p_offScreen->i_bytesPerRow + x * p_offScreen->i_bytesPerPixel + 1 ];