Commit 3232ed95 authored by Bernie Purcell's avatar Bernie Purcell Committed by Felix Paul Kühne

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: default avatarFelix Paul Kühne <fkuehne@videolan.org>
parent 9f9c62e2
...@@ -25,13 +25,6 @@ ...@@ -25,13 +25,6 @@
// Preamble // Preamble
////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////
#ifdef __x86_64__
#warning "No text renderer build! Quartztext isn't 64bit compatible!"
#warning "RE-WRITE ME!"
#else
#ifdef HAVE_CONFIG_H #ifdef HAVE_CONFIG_H
# include "config.h" # include "config.h"
#endif #endif
...@@ -73,22 +66,19 @@ static int RenderHtml( filter_t *, subpicture_region_t *, ...@@ -73,22 +66,19 @@ static int RenderHtml( filter_t *, subpicture_region_t *,
static int GetFontSize( filter_t *p_filter ); static int GetFontSize( filter_t *p_filter );
static int RenderYUVA( filter_t *p_filter, subpicture_region_t *p_region, static int RenderYUVA( filter_t *p_filter, subpicture_region_t *p_region,
UniChar *psz_utfString, uint32_t i_text_len, CFMutableAttributedStringRef p_attrString );
uint32_t i_runs, uint32_t *pi_run_lengths,
ATSUStyle *pp_styles ); static void setFontAttibutes( char *psz_fontname, int i_font_size, uint32_t i_font_color,
static ATSUStyle CreateStyle( char *psz_fontname, int i_font_size, bool b_bold, bool b_italic, bool b_underline,
uint32_t i_font_color, CFRange p_range, CFMutableAttributedStringRef p_attrString );
bool b_bold, bool b_italic,
bool b_uline );
////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////
// Module descriptor // Module descriptor
////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////
// The preferred way to set font style information is for it to come from the // 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 // subtitle file, and for it to be rendered with RenderHtml instead of
// RenderText. This module, unlike Freetype, doesn't provide any options to // RenderText.
// override the fallback font selection used when this style information is
// absent.
#define FONT_TEXT N_("Font") #define FONT_TEXT N_("Font")
#define FONT_LONGTEXT N_("Name for the font you want to use") #define FONT_LONGTEXT N_("Name for the font you want to use")
#define FONTSIZER_TEXT N_("Relative font size") #define FONTSIZER_TEXT N_("Relative font size")
...@@ -134,6 +124,26 @@ vlc_module_begin () ...@@ -134,6 +124,26 @@ vlc_module_begin ()
set_callbacks( Create, Destroy ) set_callbacks( Create, Destroy )
vlc_module_end () 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; typedef struct offscreen_bitmap_t offscreen_bitmap_t;
struct offscreen_bitmap_t struct offscreen_bitmap_t
{ {
...@@ -161,12 +171,6 @@ struct filter_sys_t ...@@ -161,12 +171,6 @@ struct filter_sys_t
int i_fonts; 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 // Create: allocates osd-text video thread output method
////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////
...@@ -285,63 +289,6 @@ static int LoadFontsFromAttachments( filter_t *p_filter ) ...@@ -285,63 +289,6 @@ static int LoadFontsFromAttachments( filter_t *p_filter )
return rv; 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 ) static char *EliminateCRLF( char *psz_string )
{ {
char *p; char *p;
...@@ -360,41 +307,15 @@ static char *EliminateCRLF( char *psz_string ) ...@@ -360,41 +307,15 @@ static char *EliminateCRLF( char *psz_string )
return 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. // Renders a text subpicture region into another one.
// It is used as pf_add_string callback in the vout method by this module // 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, static int RenderText( filter_t *p_filter, subpicture_region_t *p_region_out,
subpicture_region_t *p_region_in ) subpicture_region_t *p_region_in )
{ {
filter_sys_t *p_sys = p_filter->p_sys; filter_sys_t *p_sys = p_filter->p_sys;
UniChar *psz_utf16_str = NULL;
uint32_t i_string_length;
char *psz_string; 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; vlc_value_t val;
int i_scale = 1000; int i_scale = 1000;
...@@ -432,131 +353,437 @@ static int RenderText( filter_t *p_filter, subpicture_region_t *p_region_out, ...@@ -432,131 +353,437 @@ static int RenderText( filter_t *p_filter, subpicture_region_t *p_region_out,
i_font_size = 12; 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_x = p_region_in->i_x;
p_region_out->i_y = p_region_in->i_y; 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, CFStringRef p_cfString;
(i_font_color & 0xffffff) | int len;
((i_font_alpha & 0xff) << 24),
false, false, false ); EliminateCRLF( psz_string);
if( p_style ) p_cfString = CFStringCreateWithCString( NULL, psz_string, kCFStringEncodingUTF8 );
{ CFAttributedStringReplaceString( p_attrString, CFRangeMake(0, 0), p_cfString );
RenderYUVA( p_filter, p_region_out, psz_utf16_str, i_string_length, CFRelease( p_cfString );
1, &i_string_length, &p_style ); 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 ); RenderYUVA( p_filter, p_region_out, p_attrString );
free( psz_utf16_str );
} }
CFRelease(p_attrString);
return VLC_SUCCESS; return VLC_SUCCESS;
} }
static ATSUStyle CreateStyle( char *psz_fontname, int i_font_size, uint32_t i_font_color, static int PushFont( font_stack_t **p_font, const char *psz_name, int i_size,
bool b_bold, bool b_italic, bool b_uline ) uint32_t i_color )
{ {
ATSUStyle p_style; font_stack_t *p_new;
OSStatus status;
uint32_t i_tag_cnt; if( !p_font )
return VLC_EGENERIC;
float f_red = (float)(( i_font_color & 0x00FF0000 ) >> 16) / 255.0;
float f_green = (float)(( i_font_color & 0x0000FF00 ) >> 8) / 255.0; p_new = malloc( sizeof( font_stack_t ) );
float f_blue = (float)( i_font_color & 0x000000FF ) / 255.0; if( ! p_new )
float f_alpha = ( 255.0 - (float)(( i_font_color & 0xFF000000 ) >> 24)) / 255.0; return VLC_ENOMEM;
ATSUFontID font; p_new->p_next = NULL;
Fixed font_size = IntToFixed( i_font_size );
ATSURGBAlphaColor font_color = { f_red, f_green, f_blue, f_alpha }; if( psz_name )
Boolean bold = b_bold; p_new->psz_name = strdup( psz_name );
Boolean italic = b_italic; else
Boolean uline = b_uline; p_new->psz_name = NULL;
ATSUAttributeTag tags[] = { kATSUSizeTag, kATSURGBAlphaColorTag, kATSUQDItalicTag, p_new->i_size = i_size;
kATSUQDBoldfaceTag, kATSUQDUnderlineTag, kATSUFontTag }; p_new->i_color = i_color;
ByteCount sizes[] = { sizeof( Fixed ), sizeof( ATSURGBAlphaColor ), sizeof( Boolean ),
sizeof( Boolean ), sizeof( Boolean ), sizeof( ATSUFontID )}; if( !*p_font )
ATSUAttributeValuePtr values[] = { &font_size, &font_color, &italic, &bold, &uline, &font }; {
*p_font = p_new;
i_tag_cnt = sizeof( tags ) / sizeof( ATSUAttributeTag ); }
else
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 font_stack_t *p_last;
i_tag_cnt--;
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, static void setFontAttibutes( char *psz_fontname, int i_font_size, uint32_t i_font_color,
font_stack_t **p_fonts, bool b_bold, bool b_italic, bool b_bold, bool b_italic, bool b_underline,
bool b_uline ) 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; static void GetAttrStrFromFontStack( font_stack_t **p_fonts,
uint32_t i_font_color = p_sys->i_font_color; bool b_bold, bool b_italic, bool b_uline,
uint32_t i_karaoke_bg_color = i_font_color; /* Use it */ CFRange p_range, CFMutableAttributedStringRef p_attrString )
int i_font_size = p_sys->i_font_size; {
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, 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, setFontAttibutes( psz_fontname,
b_bold, b_italic, b_uline ); 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, static int ProcessNodes( filter_t *p_filter,
UniChar **psz_text_out, uint32_t *pi_runs, xml_reader_t *p_xml_reader,
uint32_t **ppi_run_lengths, ATSUStyle **ppp_styles, text_style_t *p_font_style,
ATSUStyle p_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 ); if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
*psz_text_out += i_string_length; 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 ) while ( ( xml_ReaderRead( p_xml_reader ) == 1 ) )
*ppp_styles = (ATSUStyle *) realloc( *ppp_styles, *pi_runs * sizeof( ATSUStyle ) ); {
else switch ( xml_ReaderNodeType( p_xml_reader ) )
*ppp_styles = (ATSUStyle *) malloc( *pi_runs * sizeof( ATSUStyle ) ); {
case XML_READER_NONE:
break;
case XML_READER_ENDELEM:
psz_node = xml_ReaderName( p_xml_reader );
(*ppp_styles)[ *pi_runs - 1 ] = p_style; if( psz_node )
{
if( !strcasecmp( "font", psz_node ) )
PopFont( &p_fonts );
else if( !strcasecmp( "b", psz_node ) )
b_bold = false;
else if( !strcasecmp( "i", psz_node ) )
b_italic = false;
else if( !strcasecmp( "u", psz_node ) )
b_uline = false;
if( *ppi_run_lengths ) free( psz_node );
*ppi_run_lengths = (uint32_t *) realloc( *ppi_run_lengths, *pi_runs * sizeof( uint32_t ) ); }
else break;
*ppi_run_lengths = (uint32_t *) malloc( *pi_runs * sizeof( uint32_t ) ); case XML_READER_STARTELEM:
psz_node = xml_ReaderName( p_xml_reader );
if( psz_node )
{
if( !strcasecmp( "font", psz_node ) )
rv = HandleFontAttributes( p_xml_reader, &p_fonts, i_scale );
else if( !strcasecmp( "b", psz_node ) )
b_bold = true;
else if( !strcasecmp( "i", psz_node ) )
b_italic = true;
else if( !strcasecmp( "u", psz_node ) )
b_uline = true;
else if( !strcasecmp( "br", psz_node ) )
{
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 );
}
break;
case XML_READER_TEXT:
psz_node = xml_ReaderValue( p_xml_reader );
if( psz_node )
{
CFStringRef p_cfString;
int len;
// Turn any multiple-whitespaces into single spaces
char *s = strpbrk( psz_node, "\t\r\n " );
while( s )
{
int i_whitespace = strspn( s, "\t\r\n " );
if( i_whitespace > 1 )
memmove( &s[1],
&s[i_whitespace],
strlen( s ) - i_whitespace + 1 );
*s++ = ' ';
s = strpbrk( s, "\t\r\n " );
}
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;
}
} }
while( VLC_SUCCESS == PopFont( &p_fonts ) );
return rv;
} }
static int RenderHtml( filter_t *p_filter, subpicture_region_t *p_region_out, static int RenderHtml( filter_t *p_filter, subpicture_region_t *p_region_out,
...@@ -618,44 +845,22 @@ static int RenderHtml( filter_t *p_filter, subpicture_region_t *p_region_out, ...@@ -618,44 +845,22 @@ static int RenderHtml( filter_t *p_filter, subpicture_region_t *p_region_out,
if( p_xml_reader ) if( p_xml_reader )
{ {
UniChar *psz_text; int i_len;
int i_len = 0;
uint32_t i_runs = 0;
uint32_t i_k_runs = 0;
uint32_t *pi_run_lengths = NULL;
uint32_t *pi_k_run_lengths = NULL;
uint32_t *pi_k_durations = NULL;
ATSUStyle *pp_styles = NULL;
psz_text = (UniChar *) malloc( strlen( p_region_in->psz_html ) *
sizeof( UniChar ) );
if( psz_text )
{
uint32_t k;
rv = ProcessNodes( p_filter, p_xml_reader,
p_region_in->p_style, psz_text, &i_len,
&i_runs, &pi_run_lengths, &pp_styles,
/* No karaoke support */
false, &i_k_runs, &pi_k_run_lengths, &pi_k_durations );
assert( pi_k_run_lengths == NULL && pi_k_durations == NULL ); 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; i_len = CFAttributedStringGetLength( p_attrString );
p_region_out->i_y = p_region_in->i_y;
if(( rv == VLC_SUCCESS ) && ( i_len > 0 )) p_region_out->i_x = p_region_in->i_x;
{ p_region_out->i_y = p_region_in->i_y;
RenderYUVA( p_filter, p_region_out, psz_text, i_len, i_runs,
pi_run_lengths, pp_styles);
}
for( k=0; k<i_runs; k++) if(( rv == VLC_SUCCESS ) && ( i_len > 0 ))
ATSUDisposeStyle( pp_styles[k] ); {
free( pp_styles ); RenderYUVA( p_filter, p_region_out, p_attrString );
free( pi_run_lengths );
free( psz_text );
} }
CFRelease(p_attrString);
xml_ReaderDelete( p_xml, p_xml_reader ); xml_ReaderDelete( p_xml, p_xml_reader );
} }
...@@ -704,9 +909,11 @@ static CGContextRef CreateOffScreenContext( int i_width, int i_height, ...@@ -704,9 +909,11 @@ static CGContextRef CreateOffScreenContext( int i_width, int i_height,
return p_context; return p_context;
} }
static offscreen_bitmap_t *Compose( int i_text_align, UniChar *psz_utf16_str, uint32_t i_text_len, static offscreen_bitmap_t *Compose( int i_text_align,
uint32_t i_runs, uint32_t *pi_run_lengths, ATSUStyle *pp_styles, CFMutableAttributedStringRef p_attrString,
int i_width, int i_height, int *pi_textblock_height ) int i_width,
int i_height,
int *pi_textblock_height )
{ {
offscreen_bitmap_t *p_offScreen = NULL; offscreen_bitmap_t *p_offScreen = NULL;
CGColorSpaceRef p_colorSpace = NULL; CGColorSpaceRef p_colorSpace = NULL;
...@@ -714,103 +921,75 @@ static offscreen_bitmap_t *Compose( int i_text_align, UniChar *psz_utf16_str, ui ...@@ -714,103 +921,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 ); p_context = CreateOffScreenContext( i_width, i_height, &p_offScreen, &p_colorSpace );
*pi_textblock_height = 0;
if( p_context ) if( p_context )
{ {
ATSUTextLayout p_textLayout; float horiz_flush;
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 );
ATSUAttributeTag tags[] = { kATSUCGContextTag, kATSULineFlushFactorTag, kATSULineWidthTag }; CGContextSetTextMatrix( p_context, CGAffineTransformIdentity );
ByteCount sizes[] = { sizeof( CGContextRef ), sizeof( Fract ), sizeof( Fixed ) };
ATSUAttributeValuePtr values[] = { &p_context, &alignment, &line_width };
int i_tag_cnt = sizeof( tags ) / sizeof( ATSUAttributeTag ); if( i_text_align == SUBPICTURE_ALIGN_RIGHT )
horiz_flush = 1.0;
if( i_text_align == SUBPICTURE_ALIGN_RIGHT ) else if( i_text_align != SUBPICTURE_ALIGN_LEFT )
{ horiz_flush = 0.5;
alignment = kATSUEndAlignment; else
} horiz_flush = 0.0;
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 // Create the framesetter with the attributed string.
ATSUSetTransientFontMatching( p_textLayout, true ); 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 ); // Create the frame and draw it into the graphics context
Fixed y = Long2Fix( i_height ); frame = CTFramesetterCreateFrame(framesetter, CFRangeMake(0, 0), p_path, NULL);
// Set the line-breaks and draw individual lines CGPathRelease(p_path);
uint32_t i_start = 0;
uint32_t i_end = i_text_len;
// Set up black outlining of the text -- // Set up black outlining of the text --
CGContextSetRGBStrokeColor( p_context, 0, 0, 0, 0.5 ); CGContextSetRGBStrokeColor( p_context, 0, 0, 0, 0.5 );
CGContextSetTextDrawingMode( p_context, kCGTextFillStroke ); CGContextSetTextDrawingMode( p_context, kCGTextFillStroke );
CGContextSetShadow( p_context, CGSizeMake( 0, 0 ), 5 );
float black_components[4] = {0, 0, 0, 1}; if( frame != NULL )
CGColorRef outlinecolor = CGColorCreate( CGColorSpaceCreateWithName( kCGColorSpaceGenericRGB ), black_components );
CGContextSetShadowWithColor (p_context, CGSizeMake( 0, 0 ), 5, outlinecolor);
CGColorRelease( outlinecolor );
do
{ {
// ATSUBreakLine will automatically pick up any manual '\n's also CFArrayRef lines;
status = ATSUBreakLine( p_textLayout, i_start, line_width, true, (UniCharArrayOffset *) &i_end ); CGPoint penPosition;
if( ( status == noErr ) || ( status == kATSULineBreakInWord ) )
{
Fixed ascent;
Fixed descent;
uint32_t i_actualSize;
// Come down far enough to fit the height of this line -- lines = CTFrameGetLines( frame );
ATSUGetLineControl( p_textLayout, i_start, kATSULineAscentTag, penPosition.y = i_height;
sizeof( Fixed ), &ascent, (ByteCount *) &i_actualSize ); for (int i=0; i<CFArrayGetCount( lines ); i++)
{
CGFloat ascent, descent, leading;
// Quartz uses an upside-down co-ordinate space -> y values decrease as CTLineRef line = (CTLineRef)CFArrayGetValueAtIndex(lines, i);
// you move down the page CTLineGetTypographicBounds(line, &ascent, &descent, &leading);
y -= ascent;
// 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 // 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 )); 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 double penOffset = CTLineGetPenOffsetForFlush(line, horiz_flush, (i_width - HORIZONTAL_MARGIN*2));
// descent penPosition.x = HORIZONTAL_MARGIN + penOffset;
ATSUGetLineControl( p_textLayout, i_start, kATSULineDescentTag, penPosition.y -= ascent;
sizeof( Fixed ), &descent, (ByteCount *) &i_actualSize ); CGContextSetTextPosition( p_context, penPosition.x, penPosition.y );
y -= descent; CTLineDraw( line, p_context );
penPosition.y -= descent + leading;
i_start = i_end;
} }
else *pi_textblock_height = i_height - penPosition.y;
break;
}
while( i_end < i_text_len );
*pi_textblock_height = i_height - Fix2Long( y );
CGContextFlush( p_context );
ATSUDisposeTextLayout( p_textLayout ); CFRelease(frame);
}
CFRelease(framesetter);
} }
CGContextFlush( p_context );
CGContextRelease( p_context ); CGContextRelease( p_context );
} }
if( p_colorSpace ) CGColorSpaceRelease( p_colorSpace ); if( p_colorSpace ) CGColorSpaceRelease( p_colorSpace );
...@@ -820,11 +999,11 @@ static offscreen_bitmap_t *Compose( int i_text_align, UniChar *psz_utf16_str, ui ...@@ -820,11 +999,11 @@ static offscreen_bitmap_t *Compose( int i_text_align, UniChar *psz_utf16_str, ui
static int GetFontSize( filter_t *p_filter ) 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, static int RenderYUVA( filter_t *p_filter, subpicture_region_t *p_region,
uint32_t i_text_len, uint32_t i_runs, uint32_t *pi_run_lengths, ATSUStyle *pp_styles ) CFMutableAttributedStringRef p_attrString )
{ {
offscreen_bitmap_t *p_offScreen = NULL; offscreen_bitmap_t *p_offScreen = NULL;
int i_textblock_height = 0; int i_textblock_height = 0;
...@@ -833,14 +1012,13 @@ static int RenderYUVA( filter_t *p_filter, subpicture_region_t *p_region, UniCha ...@@ -833,14 +1012,13 @@ static int RenderYUVA( filter_t *p_filter, subpicture_region_t *p_region, UniCha
int i_height = p_filter->fmt_out.video.i_visible_height; int i_height = p_filter->fmt_out.video.i_visible_height;
int i_text_align = p_region->i_align & 0x3; int i_text_align = p_region->i_align & 0x3;
if( !psz_utf16_str ) if( !p_attrString )
{ {
msg_Err( p_filter, "Invalid argument to RenderYUVA" ); msg_Err( p_filter, "Invalid argument to RenderYUVA" );
return VLC_EGENERIC; return VLC_EGENERIC;
} }
p_offScreen = Compose( i_text_align, psz_utf16_str, i_text_len, p_offScreen = Compose( i_text_align, p_attrString,
i_runs, pi_run_lengths, pp_styles,
i_width, i_height, &i_textblock_height ); i_width, i_height, &i_textblock_height );
if( !p_offScreen ) if( !p_offScreen )
...@@ -903,5 +1081,3 @@ static int RenderYUVA( filter_t *p_filter, subpicture_region_t *p_region, UniCha ...@@ -903,5 +1081,3 @@ static int RenderYUVA( filter_t *p_filter, subpicture_region_t *p_region, UniCha
return VLC_SUCCESS; return VLC_SUCCESS;
} }
#endif
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment