X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=modules%2Fmisc%2Fquartztext.c;h=b1dd12ade814bb05b53cab008f14a5dd917ee8c0;hb=6e2cff1dbbae76b5901085a1331f51723864a377;hp=033028668d48c5e2dac8d5cbc126fd916a8d0deb;hpb=5fe3825b21ebf4ad84fdc80f3db6436682b0b577;p=vlc diff --git a/modules/misc/quartztext.c b/modules/misc/quartztext.c index 033028668d..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 ); + FONTSIZER_LONGTEXT, false ) change_integer_list( pi_sizes, ppsz_sizes_text, NULL ); add_integer( "quartztext-color", 0x00FFFFFF, NULL, COLOR_TEXT, - COLOR_LONGTEXT, false ); + COLOR_LONGTEXT, false ) change_integer_list( pi_color_values, ppsz_color_descriptions, NULL ); - set_capability( "text renderer", 120 ); - add_shortcut( "text" ); - set_callbacks( Create, Destroy ); -vlc_module_end(); + 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,199 +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 const struct { - const char *psz_name; - uint32_t i_value; -} p_html_colors[] = { - /* Official html colors */ - { "Aqua", 0x00FFFF }, - { "Black", 0x000000 }, - { "Blue", 0x0000FF }, - { "Fuchsia", 0xFF00FF }, - { "Gray", 0x808080 }, - { "Green", 0x008000 }, - { "Lime", 0x00FF00 }, - { "Maroon", 0x800000 }, - { "Navy", 0x000080 }, - { "Olive", 0x808000 }, - { "Purple", 0x800080 }, - { "Red", 0xFF0000 }, - { "Silver", 0xC0C0C0 }, - { "Teal", 0x008080 }, - { "White", 0xFFFFFF }, - { "Yellow", 0xFFFF00 }, - - /* Common ones */ - { "AliceBlue", 0xF0F8FF }, - { "AntiqueWhite", 0xFAEBD7 }, - { "Aqua", 0x00FFFF }, - { "Aquamarine", 0x7FFFD4 }, - { "Azure", 0xF0FFFF }, - { "Beige", 0xF5F5DC }, - { "Bisque", 0xFFE4C4 }, - { "Black", 0x000000 }, - { "BlanchedAlmond", 0xFFEBCD }, - { "Blue", 0x0000FF }, - { "BlueViolet", 0x8A2BE2 }, - { "Brown", 0xA52A2A }, - { "BurlyWood", 0xDEB887 }, - { "CadetBlue", 0x5F9EA0 }, - { "Chartreuse", 0x7FFF00 }, - { "Chocolate", 0xD2691E }, - { "Coral", 0xFF7F50 }, - { "CornflowerBlue", 0x6495ED }, - { "Cornsilk", 0xFFF8DC }, - { "Crimson", 0xDC143C }, - { "Cyan", 0x00FFFF }, - { "DarkBlue", 0x00008B }, - { "DarkCyan", 0x008B8B }, - { "DarkGoldenRod", 0xB8860B }, - { "DarkGray", 0xA9A9A9 }, - { "DarkGrey", 0xA9A9A9 }, - { "DarkGreen", 0x006400 }, - { "DarkKhaki", 0xBDB76B }, - { "DarkMagenta", 0x8B008B }, - { "DarkOliveGreen", 0x556B2F }, - { "Darkorange", 0xFF8C00 }, - { "DarkOrchid", 0x9932CC }, - { "DarkRed", 0x8B0000 }, - { "DarkSalmon", 0xE9967A }, - { "DarkSeaGreen", 0x8FBC8F }, - { "DarkSlateBlue", 0x483D8B }, - { "DarkSlateGray", 0x2F4F4F }, - { "DarkSlateGrey", 0x2F4F4F }, - { "DarkTurquoise", 0x00CED1 }, - { "DarkViolet", 0x9400D3 }, - { "DeepPink", 0xFF1493 }, - { "DeepSkyBlue", 0x00BFFF }, - { "DimGray", 0x696969 }, - { "DimGrey", 0x696969 }, - { "DodgerBlue", 0x1E90FF }, - { "FireBrick", 0xB22222 }, - { "FloralWhite", 0xFFFAF0 }, - { "ForestGreen", 0x228B22 }, - { "Fuchsia", 0xFF00FF }, - { "Gainsboro", 0xDCDCDC }, - { "GhostWhite", 0xF8F8FF }, - { "Gold", 0xFFD700 }, - { "GoldenRod", 0xDAA520 }, - { "Gray", 0x808080 }, - { "Grey", 0x808080 }, - { "Green", 0x008000 }, - { "GreenYellow", 0xADFF2F }, - { "HoneyDew", 0xF0FFF0 }, - { "HotPink", 0xFF69B4 }, - { "IndianRed", 0xCD5C5C }, - { "Indigo", 0x4B0082 }, - { "Ivory", 0xFFFFF0 }, - { "Khaki", 0xF0E68C }, - { "Lavender", 0xE6E6FA }, - { "LavenderBlush", 0xFFF0F5 }, - { "LawnGreen", 0x7CFC00 }, - { "LemonChiffon", 0xFFFACD }, - { "LightBlue", 0xADD8E6 }, - { "LightCoral", 0xF08080 }, - { "LightCyan", 0xE0FFFF }, - { "LightGoldenRodYellow", 0xFAFAD2 }, - { "LightGray", 0xD3D3D3 }, - { "LightGrey", 0xD3D3D3 }, - { "LightGreen", 0x90EE90 }, - { "LightPink", 0xFFB6C1 }, - { "LightSalmon", 0xFFA07A }, - { "LightSeaGreen", 0x20B2AA }, - { "LightSkyBlue", 0x87CEFA }, - { "LightSlateGray", 0x778899 }, - { "LightSlateGrey", 0x778899 }, - { "LightSteelBlue", 0xB0C4DE }, - { "LightYellow", 0xFFFFE0 }, - { "Lime", 0x00FF00 }, - { "LimeGreen", 0x32CD32 }, - { "Linen", 0xFAF0E6 }, - { "Magenta", 0xFF00FF }, - { "Maroon", 0x800000 }, - { "MediumAquaMarine", 0x66CDAA }, - { "MediumBlue", 0x0000CD }, - { "MediumOrchid", 0xBA55D3 }, - { "MediumPurple", 0x9370D8 }, - { "MediumSeaGreen", 0x3CB371 }, - { "MediumSlateBlue", 0x7B68EE }, - { "MediumSpringGreen", 0x00FA9A }, - { "MediumTurquoise", 0x48D1CC }, - { "MediumVioletRed", 0xC71585 }, - { "MidnightBlue", 0x191970 }, - { "MintCream", 0xF5FFFA }, - { "MistyRose", 0xFFE4E1 }, - { "Moccasin", 0xFFE4B5 }, - { "NavajoWhite", 0xFFDEAD }, - { "Navy", 0x000080 }, - { "OldLace", 0xFDF5E6 }, - { "Olive", 0x808000 }, - { "OliveDrab", 0x6B8E23 }, - { "Orange", 0xFFA500 }, - { "OrangeRed", 0xFF4500 }, - { "Orchid", 0xDA70D6 }, - { "PaleGoldenRod", 0xEEE8AA }, - { "PaleGreen", 0x98FB98 }, - { "PaleTurquoise", 0xAFEEEE }, - { "PaleVioletRed", 0xD87093 }, - { "PapayaWhip", 0xFFEFD5 }, - { "PeachPuff", 0xFFDAB9 }, - { "Peru", 0xCD853F }, - { "Pink", 0xFFC0CB }, - { "Plum", 0xDDA0DD }, - { "PowderBlue", 0xB0E0E6 }, - { "Purple", 0x800080 }, - { "Red", 0xFF0000 }, - { "RosyBrown", 0xBC8F8F }, - { "RoyalBlue", 0x4169E1 }, - { "SaddleBrown", 0x8B4513 }, - { "Salmon", 0xFA8072 }, - { "SandyBrown", 0xF4A460 }, - { "SeaGreen", 0x2E8B57 }, - { "SeaShell", 0xFFF5EE }, - { "Sienna", 0xA0522D }, - { "Silver", 0xC0C0C0 }, - { "SkyBlue", 0x87CEEB }, - { "SlateBlue", 0x6A5ACD }, - { "SlateGray", 0x708090 }, - { "SlateGrey", 0x708090 }, - { "Snow", 0xFFFAFA }, - { "SpringGreen", 0x00FF7F }, - { "SteelBlue", 0x4682B4 }, - { "Tan", 0xD2B48C }, - { "Teal", 0x008080 }, - { "Thistle", 0xD8BFD8 }, - { "Tomato", 0xFF6347 }, - { "Turquoise", 0x40E0D0 }, - { "Violet", 0xEE82EE }, - { "Wheat", 0xF5DEB3 }, - { "White", 0xFFFFFF }, - { "WhiteSmoke", 0xF5F5F5 }, - { "Yellow", 0xFFFF00 }, - { "YellowGreen", 0x9ACD32 }, - - { NULL, 0 } -}; - static int HandleFontAttributes( xml_reader_t *p_xml_reader, font_stack_t **p_fonts, int i_scale ) { @@ -825,24 +509,11 @@ static int HandleFontAttributes( xml_reader_t *p_xml_reader, else i_font_size = atoi( psz_value ); } - else if( !strcasecmp( "color", psz_name ) ) + else if( !strcasecmp( "color", psz_name ) && + ( psz_value[0] == '#' ) ) { - if( psz_value[0] == '#' ) - { - i_font_color = strtol( psz_value + 1, NULL, 16 ); - i_font_color &= 0x00ffffff; - } - else - { - for( int i = 0; p_html_colors[i].psz_name != NULL; i++ ) - { - if( !strncasecmp( psz_value, p_html_colors[i].psz_name, strlen(p_html_colors[i].psz_name) ) ) - { - i_font_color = p_html_colors[i].i_value; - break; - } - } - } + i_font_color = strtol( psz_value + 1, NULL, 16 ); + i_font_color &= 0x00ffffff; } else if( !strcasecmp( "alpha", psz_name ) && ( psz_value[0] == '#' ) ) @@ -850,9 +521,9 @@ static int HandleFontAttributes( xml_reader_t *p_xml_reader, i_font_alpha = strtol( psz_value + 1, NULL, 16 ); i_font_alpha &= 0xff; } + free( psz_name ); + free( psz_value ); } - free( psz_name ); - free( psz_value ); } rv = PushFont( p_fonts, psz_fontname, @@ -864,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; @@ -923,6 +688,7 @@ static int ProcessNodes( filter_t *p_filter, break; case XML_READER_ENDELEM: psz_node = xml_ReaderName( p_xml_reader ); + if( psz_node ) { if( !strcasecmp( "font", psz_node ) ) @@ -951,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 ); } @@ -979,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 " ); @@ -996,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)++; - - 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 ) ); + 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 ); - (*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; @@ -1076,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; } @@ -1087,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 ); } @@ -1145,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 ) { @@ -1159,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; } @@ -1174,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; @@ -1184,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) + float horiz_flush; - Fract alignment = kATSUStartAlignment; - Fixed line_width = Long2Fix( i_width - HORIZONTAL_MARGIN * 2 ); + CGContextSetTextMatrix( p_context, CGAffineTransformIdentity ); - ATSUAttributeTag tags[] = { kATSUCGContextTag, kATSULineFlushFactorTag, kATSULineWidthTag }; - ByteCount sizes[] = { sizeof( CGContextRef ), sizeof( Fract ), sizeof( Fixed ) }; - ATSUAttributeValuePtr values[] = { &p_context, &alignment, &line_width }; + 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; - int i_tag_cnt = sizeof( tags ) / sizeof( ATSUAttributeTag ); - - 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 ); - - // 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; + 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; - i_start = i_end; } - else - break; - } - while( i_end < i_text_len ); + *pi_textblock_height = i_height - penPosition.y; - *pi_textblock_height = i_height - Fix2Long( y ); - CGContextFlush( p_context ); - - ATSUDisposeTextLayout( p_textLayout ); + CFRelease(frame); + } + CFRelease(framesetter); } - + CGContextFlush( p_context ); CGContextRelease( p_context ); } if( p_colorSpace ) CGColorSpaceRelease( p_colorSpace ); @@ -1287,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 ) @@ -1318,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 ];