Commit 3232ed95 authored by bitmap's avatar bitmap Committed by Felix Paul Kühne
Browse files

Rewrite quartztext to use CoreText API instead of ATSUI. ATSUI only works in...


Rewrite quartztext to use CoreText API instead of ATSUI. ATSUI only works in 32bit applications, but CoreText requires Mac OS X 10.5 or later. This change makes this module unusable on older versions of Mac OS X.
Signed-off-by: Felix Paul Kühne's avatarFelix Paul Kühne <fkuehne@videolan.org>
parent 9f9c62e2
......@@ -25,13 +25,6 @@
// Preamble
//////////////////////////////////////////////////////////////////////////////
#ifdef __x86_64__
#warning "No text renderer build! Quartztext isn't 64bit compatible!"
#warning "RE-WRITE ME!"
#else
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
......@@ -73,22 +66,19 @@ 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")
......@@ -134,6 +124,26 @@ vlc_module_begin ()
set_callbacks( Create, Destroy )
vlc_module_end ()
typedef struct font_stack_t font_stack_t;
struct font_stack_t
{
char *psz_name;
int i_size;
uint32_t i_color; // ARGB
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
{
......@@ -161,12 +171,6 @@ struct filter_sys_t
int i_fonts;
};
#define UCHAR UniChar
#define TR_DEFAULT_FONT p_sys->psz_font_name
#define TR_FONT_STYLE_PTR ATSUStyle
#include "text_renderer.h"
//////////////////////////////////////////////////////////////////////////////
// Create: allocates osd-text video thread output method
//////////////////////////////////////////////////////////////////////////////
......@@ -285,63 +289,6 @@ static int LoadFontsFromAttachments( filter_t *p_filter )
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;
}
#endif
static char *EliminateCRLF( char *psz_string )
{
char *p;
......@@ -360,41 +307,15 @@ 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;
vlc_value_t val;
int i_scale = 1000;
......@@ -432,131 +353,437 @@ static int RenderText( filter_t *p_filter, subpicture_region_t *p_region_out,
i_font_size = 12;
}
ConvertToUTF16( EliminateCRLF( psz_string ), &i_string_length, &psz_utf16_str );
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 )
CFMutableAttributedStringRef p_attrString = CFAttributedStringCreateMutable(kCFAllocatorDefault, 0);
if( p_attrString )
{
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 );
}
CFStringRef p_cfString;
int len;
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, FALSE, FALSE, FALSE,
CFRangeMake( 0, len ), p_attrString);
ATSUDisposeStyle( p_style );
free( psz_utf16_str );
RenderYUVA( p_filter, p_region_out, p_attrString );
}
CFRelease(p_attrString);
return VLC_SUCCESS;
}
static ATSUStyle CreateStyle( char *psz_fontname, int i_font_size, uint32_t i_font_color,
bool b_bold, bool b_italic, bool b_uline )
static int PushFont( font_stack_t **p_font, const char *psz_name, int i_size,
uint32_t i_color )
{
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 )
font_stack_t *p_new;
if( !p_font )
return VLC_EGENERIC;
p_new = malloc( sizeof( font_stack_t ) );
if( ! p_new )
return VLC_ENOMEM;
p_new->p_next = NULL;
if( psz_name )
p_new->psz_name = strdup( psz_name );
else
p_new->psz_name = NULL;
p_new->i_size = i_size;
p_new->i_color = i_color;
if( !*p_font )
{
*p_font = p_new;
}
else
{
// If we can't find a suitable font, just do everything else
i_tag_cnt--;
font_stack_t *p_last;
for( p_last = *p_font;
p_last->p_next;
p_last = p_last->p_next )
;
p_last->p_next = p_new;
}
return VLC_SUCCESS;
}
static int PopFont( font_stack_t **p_font )
{
font_stack_t *p_last, *p_next_to_last;
if( !p_font || !*p_font )
return VLC_EGENERIC;
if( noErr == ATSUCreateStyle( &p_style ) )
p_next_to_last = NULL;
for( p_last = *p_font;
p_last->p_next;
p_last = p_last->p_next )
{
if( noErr == ATSUSetAttributes( p_style, i_tag_cnt, tags, sizes, values ) )
p_next_to_last = p_last;
}
if( p_next_to_last )
p_next_to_last->p_next = NULL;
else
*p_font = NULL;
free( p_last->psz_name );
free( p_last );
return VLC_SUCCESS;
}
static int PeekFont( font_stack_t **p_font, char **psz_name, int *i_size,
uint32_t *i_color )
{
font_stack_t *p_last;
if( !p_font || !*p_font )
return VLC_EGENERIC;
for( p_last=*p_font;
p_last->p_next;
p_last=p_last->p_next )
;
*psz_name = p_last->psz_name;
*i_size = p_last->i_size;
*i_color = p_last->i_color;
return VLC_SUCCESS;
}
static int HandleFontAttributes( xml_reader_t *p_xml_reader,
font_stack_t **p_fonts, int i_scale )
{
int rv;
char *psz_fontname = NULL;
uint32_t i_font_color = 0xffffff;
int i_font_alpha = 0;
int i_font_size = 24;
// Default all attributes to the top font in the stack -- in case not
// all attributes are specified in the sub-font
if( VLC_SUCCESS == PeekFont( p_fonts,
&psz_fontname,
&i_font_size,
&i_font_color ))
{
psz_fontname = strdup( psz_fontname );
i_font_size = i_font_size * 1000 / i_scale;
}
i_font_alpha = (i_font_color >> 24) & 0xff;
i_font_color &= 0x00ffffff;
while ( xml_ReaderNextAttr( p_xml_reader ) == VLC_SUCCESS )
{
char *psz_name = xml_ReaderName( p_xml_reader );
char *psz_value = xml_ReaderValue( p_xml_reader );
if( psz_name && psz_value )
{
return p_style;
if( !strcasecmp( "face", psz_name ) )
{
if( psz_fontname ) free( psz_fontname );
psz_fontname = strdup( psz_value );
}
else if( !strcasecmp( "size", psz_name ) )
{
if( ( *psz_value == '+' ) || ( *psz_value == '-' ) )
{
int i_value = atoi( psz_value );
if( ( i_value >= -5 ) && ( i_value <= 5 ) )
i_font_size += ( i_value * i_font_size ) / 10;
else if( i_value < -5 )
i_font_size = - i_value;
else if( i_value > 5 )
i_font_size = i_value;
}
else
i_font_size = atoi( psz_value );
}
else if( !strcasecmp( "color", psz_name ) &&
( psz_value[0] == '#' ) )
{
i_font_color = strtol( psz_value + 1, NULL, 16 );
i_font_color &= 0x00ffffff;
}
else if( !strcasecmp( "alpha", psz_name ) &&
( psz_value[0] == '#' ) )
{
i_font_alpha = strtol( psz_value + 1, NULL, 16 );
i_font_alpha &= 0xff;
}
free( psz_name );
free( psz_value );
}
ATSUDisposeStyle( p_style );
}
return NULL;
rv = PushFont( p_fonts,
psz_fontname,
i_font_size * i_scale / 1000,
(i_font_color & 0xffffff) | ((i_font_alpha & 0xff) << 24) );
free( psz_fontname );
return rv;
}
static ATSUStyle GetStyleFromFontStack( filter_sys_t *p_sys,
font_stack_t **p_fonts, bool b_bold, bool b_italic,
bool b_uline )
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 )
{
ATSUStyle p_style = NULL;
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 );
}
char *psz_fontname = NULL;
uint32_t i_font_color = p_sys->i_font_color;
uint32_t i_karaoke_bg_color = i_font_color; /* Use it */
int i_font_size = p_sys->i_font_size;
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, &i_karaoke_bg_color ))
&i_font_color ))
{
p_style = CreateStyle( psz_fontname, i_font_size, i_font_color,
b_bold, b_italic, b_uline );
setFontAttibutes( psz_fontname,
i_font_size,
i_font_color,
b_bold, b_italic, b_uline,
p_range,
p_attrString );
}
return p_style;
}
static void SetupLine( filter_t *p_filter, const char *psz_text_in,
UniChar **psz_text_out, uint32_t *pi_runs,
uint32_t **ppi_run_lengths, ATSUStyle **ppp_styles,
ATSUStyle p_style )
static int ProcessNodes( filter_t *p_filter,
xml_reader_t *p_xml_reader,
text_style_t *p_font_style,
CFMutableAttributedStringRef p_attrString )
{
uint32_t i_string_length = 0;
int rv = VLC_SUCCESS;
filter_sys_t *p_sys = p_filter->p_sys;
font_stack_t *p_fonts = NULL;
vlc_value_t val;
int i_scale = 1000;
char *psz_node = NULL;
bool b_italic = false;
bool b_bold = false;
bool b_uline = false;
ConvertToUTF16( psz_text_in, &i_string_length, psz_text_out );
*psz_text_out += i_string_length;
if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
i_scale = val.i_int;
if( ppp_styles && ppi_run_lengths )
if( p_font_style )
{
(*pi_runs)++;
rv = PushFont( &p_fonts,
p_font_style->psz_fontname,
p_font_style->i_font_size * i_scale / 1000,
(p_font_style->i_font_color & 0xffffff) |
((p_font_style->i_font_alpha & 0xff) << 24) );
if( p_font_style->i_style_flags & STYLE_BOLD )
b_bold = true;
if( p_font_style->i_style_flags & STYLE_ITALIC )
b_italic = true;
if( p_font_style->i_style_flags & STYLE_UNDERLINE )
b_uline = true;
}
else
{
rv = PushFont( &p_fonts,
p_sys->psz_font_name,
p_sys->i_font_size,
p_sys->i_font_color );
}
if( rv != VLC_SUCCESS )
return rv;
if( *ppp_styles )
*ppp_styles = (ATSUStyle *) realloc( *ppp_styles, *pi_runs * sizeof( ATSUStyle ) );
else
*ppp_styles = (ATSUStyle *) malloc( *pi_runs * sizeof( ATSUStyle ) );
while ( ( xml_ReaderRead( p_xml_reader ) == 1 ) )
{
switch ( xml_ReaderNodeType( p_xml_reader ) )
{
case XML_READER_NONE:
break;
case XML_READER_ENDELEM: