quartztext.c
上传用户:kjfoods
上传日期:2020-07-06
资源大小:29949k
文件大小:33k
源码类别:

midi

开发平台:

Unix_Linux

  1. /*****************************************************************************
  2.  * quartztext.c : Put text on the video, using Mac OS X Quartz Engine
  3.  *****************************************************************************
  4.  * Copyright (C) 2007, 2009 the VideoLAN team
  5.  * $Id: 9f731382ac40010541e73de19c3892af8ae070d5 $
  6.  *
  7.  * Authors: Bernie Purcell <bitmap@videolan.org>
  8.  *
  9.  * This program is free software; you can redistribute it and/or modify
  10.  * it under the terms of the GNU General Public License as published by
  11.  * the Free Software Foundation; either version 2 of the License, or
  12.  * (at your option) any later version.
  13.  *
  14.  * This program is distributed in the hope that it will be useful,
  15.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  17.  * GNU General Public License for more details.
  18.  *
  19.  * You should have received a copy of the GNU General Public License
  20.  * along with this program; if not, write to the Free Software
  21.  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
  22.  *****************************************************************************/
  23. #ifdef __x86_64__
  24. #warning "No text renderer build! Quartztext isn't 64bit compatible!"
  25. #warning "RE-WRITE ME!"
  26. #else
  27. //////////////////////////////////////////////////////////////////////////////
  28. // Preamble
  29. //////////////////////////////////////////////////////////////////////////////
  30. #ifdef HAVE_CONFIG_H
  31. # include "config.h"
  32. #endif
  33. #include <vlc_common.h>
  34. #include <vlc_plugin.h>
  35. #include <vlc_vout.h>
  36. #include <vlc_osd.h>
  37. #include <vlc_block.h>
  38. #include <vlc_filter.h>
  39. #include <vlc_stream.h>
  40. #include <vlc_xml.h>
  41. #include <vlc_input.h>
  42. #include <vlc_strings.h>
  43. #include <math.h>
  44. #include <Carbon/Carbon.h>
  45. #define DEFAULT_FONT           "Arial Black"
  46. #define DEFAULT_FONT_COLOR     0xffffff
  47. #define DEFAULT_REL_FONT_SIZE  16
  48. #define VERTICAL_MARGIN 3
  49. #define HORIZONTAL_MARGIN 10
  50. //////////////////////////////////////////////////////////////////////////////
  51. // Local prototypes
  52. //////////////////////////////////////////////////////////////////////////////
  53. static int  Create ( vlc_object_t * );
  54. static void Destroy( vlc_object_t * );
  55. static int LoadFontsFromAttachments( filter_t *p_filter );
  56. static int RenderText( filter_t *, subpicture_region_t *,
  57.                        subpicture_region_t * );
  58. static int RenderHtml( filter_t *, subpicture_region_t *,
  59.                        subpicture_region_t * );
  60. static int GetFontSize( filter_t *p_filter );
  61. static int RenderYUVA( filter_t *p_filter, subpicture_region_t *p_region,
  62.                        UniChar *psz_utfString, uint32_t i_text_len,
  63.                        uint32_t i_runs, uint32_t *pi_run_lengths,
  64.                        ATSUStyle *pp_styles );
  65. static ATSUStyle CreateStyle( char *psz_fontname, int i_font_size,
  66.                               uint32_t i_font_color,
  67.                               bool b_bold, bool b_italic,
  68.                               bool b_uline );
  69. //////////////////////////////////////////////////////////////////////////////
  70. // Module descriptor
  71. //////////////////////////////////////////////////////////////////////////////
  72. // The preferred way to set font style information is for it to come from the
  73. // subtitle file, and for it to be rendered with RenderHtml instead of
  74. // RenderText. This module, unlike Freetype, doesn't provide any options to
  75. // override the fallback font selection used when this style information is
  76. // absent.
  77. #define FONT_TEXT N_("Font")
  78. #define FONT_LONGTEXT N_("Name for the font you want to use")
  79. #define FONTSIZER_TEXT N_("Relative font size")
  80. #define FONTSIZER_LONGTEXT N_("This is the relative default size of the " 
  81.     "fonts that will be rendered on the video. If absolute font size is set, "
  82.     "relative size will be overriden." )
  83. #define COLOR_TEXT N_("Text default color")
  84. #define COLOR_LONGTEXT N_("The color of the text that will be rendered on "
  85.     "the video. This must be an hexadecimal (like HTML colors). The first two "
  86.     "chars are for red, then green, then blue. #000000 = black, #FF0000 = red,"
  87.     " #00FF00 = green, #FFFF00 = yellow (red + green), #FFFFFF = white" )
  88. static const int pi_color_values[] = {
  89.   0x00000000, 0x00808080, 0x00C0C0C0, 0x00FFFFFF, 0x00800000,
  90.   0x00FF0000, 0x00FF00FF, 0x00FFFF00, 0x00808000, 0x00008000, 0x00008080,
  91.   0x0000FF00, 0x00800080, 0x00000080, 0x000000FF, 0x0000FFFF };
  92. static const char *const ppsz_color_descriptions[] = {
  93.   N_("Black"), N_("Gray"), N_("Silver"), N_("White"), N_("Maroon"),
  94.   N_("Red"), N_("Fuchsia"), N_("Yellow"), N_("Olive"), N_("Green"), N_("Teal"),
  95.   N_("Lime"), N_("Purple"), N_("Navy"), N_("Blue"), N_("Aqua") };
  96. static const int pi_sizes[] = { 20, 18, 16, 12, 6 };
  97. static const char *const ppsz_sizes_text[] = {
  98.     N_("Smaller"), N_("Small"), N_("Normal"), N_("Large"), N_("Larger") };
  99. vlc_module_begin ()
  100.     set_shortname( N_("Mac Text renderer"))
  101.     set_description( N_("Quartz font renderer") )
  102.     set_category( CAT_VIDEO )
  103.     set_subcategory( SUBCAT_VIDEO_SUBPIC )
  104.     add_string( "quartztext-font", DEFAULT_FONT, NULL, FONT_TEXT, FONT_LONGTEXT,
  105.               false )
  106.     add_integer( "quartztext-rel-fontsize", DEFAULT_REL_FONT_SIZE, NULL, FONTSIZER_TEXT,
  107.                  FONTSIZER_LONGTEXT, false )
  108.         change_integer_list( pi_sizes, ppsz_sizes_text, NULL );
  109.     add_integer( "quartztext-color", 0x00FFFFFF, NULL, COLOR_TEXT,
  110.                  COLOR_LONGTEXT, false )
  111.         change_integer_list( pi_color_values, ppsz_color_descriptions, NULL );
  112.     set_capability( "text renderer", 150 )
  113.     add_shortcut( "text" )
  114.     set_callbacks( Create, Destroy )
  115. vlc_module_end ()
  116. typedef struct offscreen_bitmap_t offscreen_bitmap_t;
  117. struct offscreen_bitmap_t
  118. {
  119.     uint8_t       *p_data;
  120.     int            i_bitsPerChannel;
  121.     int            i_bitsPerPixel;
  122.     int            i_bytesPerPixel;
  123.     int            i_bytesPerRow;
  124. };
  125. //////////////////////////////////////////////////////////////////////////////
  126. // filter_sys_t: quartztext local data
  127. //////////////////////////////////////////////////////////////////////////////
  128. // This structure is part of the video output thread descriptor.
  129. // It describes the freetype specific properties of an output thread.
  130. //////////////////////////////////////////////////////////////////////////////
  131. struct filter_sys_t
  132. {
  133.     char          *psz_font_name;
  134.     uint8_t        i_font_opacity;
  135.     int            i_font_color;
  136.     int            i_font_size;
  137.     ATSFontContainerRef    *p_fonts;
  138.     int                     i_fonts;
  139. };
  140. #define UCHAR UniChar
  141. #define TR_DEFAULT_FONT p_sys->psz_font_name
  142. #define TR_FONT_STYLE_PTR ATSUStyle
  143. #include "text_renderer.h"
  144. //////////////////////////////////////////////////////////////////////////////
  145. // Create: allocates osd-text video thread output method
  146. //////////////////////////////////////////////////////////////////////////////
  147. // This function allocates and initializes a Clone vout method.
  148. //////////////////////////////////////////////////////////////////////////////
  149. static int Create( vlc_object_t *p_this )
  150. {
  151.     filter_t *p_filter = (filter_t *)p_this;
  152.     filter_sys_t *p_sys;
  153.     // Allocate structure
  154.     p_filter->p_sys = p_sys = malloc( sizeof( filter_sys_t ) );
  155.     if( !p_sys )
  156.         return VLC_ENOMEM;
  157.     p_sys->psz_font_name  = var_CreateGetString( p_this, "quartztext-font" );
  158.     p_sys->i_font_opacity = 255;
  159.     p_sys->i_font_color = __MAX( __MIN( var_CreateGetInteger( p_this, "quartztext-color" ) , 0xFFFFFF ), 0 );
  160.     p_sys->i_font_size    = GetFontSize( p_filter );
  161.     p_filter->pf_render_text = RenderText;
  162.     p_filter->pf_render_html = RenderHtml;
  163.     p_sys->p_fonts = NULL;
  164.     p_sys->i_fonts = 0;
  165.     LoadFontsFromAttachments( p_filter );
  166.     return VLC_SUCCESS;
  167. }
  168. //////////////////////////////////////////////////////////////////////////////
  169. // Destroy: destroy Clone video thread output method
  170. //////////////////////////////////////////////////////////////////////////////
  171. // Clean up all data and library connections
  172. //////////////////////////////////////////////////////////////////////////////
  173. static void Destroy( vlc_object_t *p_this )
  174. {
  175.     filter_t *p_filter = (filter_t *)p_this;
  176.     filter_sys_t *p_sys = p_filter->p_sys;
  177.     if( p_sys->p_fonts )
  178.     {
  179.         int   k;
  180.         for( k = 0; k < p_sys->i_fonts; k++ )
  181.         {
  182.             ATSFontDeactivate( p_sys->p_fonts[k], NULL, kATSOptionFlagsDefault );
  183.         }
  184.         free( p_sys->p_fonts );
  185.     }
  186.     free( p_sys->psz_font_name );
  187.     free( p_sys );
  188. }
  189. //////////////////////////////////////////////////////////////////////////////
  190. // Make any TTF/OTF fonts present in the attachments of the media file
  191. // available to the Quartz engine for text rendering
  192. //////////////////////////////////////////////////////////////////////////////
  193. static int LoadFontsFromAttachments( filter_t *p_filter )
  194. {
  195.     filter_sys_t         *p_sys = p_filter->p_sys;
  196.     input_thread_t       *p_input;
  197.     input_attachment_t  **pp_attachments;
  198.     int                   i_attachments_cnt;
  199.     int                   k;
  200.     int                   rv = VLC_SUCCESS;
  201.     p_input = (input_thread_t *)vlc_object_find( p_filter, VLC_OBJECT_INPUT, FIND_PARENT );
  202.     if( ! p_input )
  203.         return VLC_EGENERIC;
  204.     if( VLC_SUCCESS != input_Control( p_input, INPUT_GET_ATTACHMENTS, &pp_attachments, &i_attachments_cnt ))
  205.     {
  206.         vlc_object_release(p_input);
  207.         return VLC_EGENERIC;
  208.     }
  209.     p_sys->i_fonts = 0;
  210.     p_sys->p_fonts = malloc( i_attachments_cnt * sizeof( ATSFontContainerRef ) );
  211.     if(! p_sys->p_fonts )
  212.         rv = VLC_ENOMEM;
  213.     for( k = 0; k < i_attachments_cnt; k++ )
  214.     {
  215.         input_attachment_t *p_attach = pp_attachments[k];
  216.         if( p_sys->p_fonts )
  217.         {
  218.             if(( !strcmp( p_attach->psz_mime, "application/x-truetype-font" ) || // TTF
  219.                  !strcmp( p_attach->psz_mime, "application/x-font-otf" ) ) &&    // OTF
  220.                ( p_attach->i_data > 0 ) &&
  221.                ( p_attach->p_data != NULL ) )
  222.             {
  223.                 ATSFontContainerRef  container;
  224.                 if( noErr == ATSFontActivateFromMemory( p_attach->p_data,
  225.                                                         p_attach->i_data,
  226.                                                         kATSFontContextLocal,
  227.                                                         kATSFontFormatUnspecified,
  228.                                                         NULL,
  229.                                                         kATSOptionFlagsDefault,
  230.                                                         &container ))
  231.                 {
  232.                     p_sys->p_fonts[ p_sys->i_fonts++ ] = container;
  233.                 }
  234.             }
  235.         }
  236.         vlc_input_attachment_Delete( p_attach );
  237.     }
  238.     free( pp_attachments );
  239.     vlc_object_release(p_input);
  240.     return rv;
  241. }
  242. #if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_4
  243. // Original version of these functions available on:
  244. // http://developer.apple.com/documentation/Carbon/Conceptual/QuickDrawToQuartz2D/tq_color/chapter_4_section_3.html
  245. #define kGenericRGBProfilePathStr "/System/Library/ColorSync/Profiles/Generic RGB Profile.icc"
  246. static CMProfileRef OpenGenericProfile( void )
  247. {
  248.     static CMProfileRef cached_rgb_prof = NULL;
  249.     // Create the profile reference only once
  250.     if( cached_rgb_prof == NULL )
  251.     {
  252.         OSStatus            err;
  253.         CMProfileLocation   loc;
  254.         loc.locType = cmPathBasedProfile;
  255.         strcpy( loc.u.pathLoc.path, kGenericRGBProfilePathStr );
  256.         err = CMOpenProfile( &cached_rgb_prof, &loc );
  257.         if( err != noErr )
  258.         {
  259.             cached_rgb_prof = NULL;
  260.         }
  261.     }
  262.     if( cached_rgb_prof )
  263.     {
  264.         // Clone the profile reference so that the caller has
  265.         // their own reference, not our cached one.
  266.         CMCloneProfileRef( cached_rgb_prof );
  267.     }
  268.     return cached_rgb_prof;
  269. }
  270. static CGColorSpaceRef CreateGenericRGBColorSpace( void )
  271. {
  272.     static CGColorSpaceRef p_generic_rgb_cs = NULL;
  273.     if( p_generic_rgb_cs == NULL )
  274.     {
  275.         CMProfileRef generic_rgb_prof = OpenGenericProfile();
  276.         if( generic_rgb_prof )
  277.         {
  278.             p_generic_rgb_cs = CGColorSpaceCreateWithPlatformColorSpace( generic_rgb_prof );
  279.             CMCloseProfile( generic_rgb_prof );
  280.         }
  281.     }
  282.     return p_generic_rgb_cs;
  283. }
  284. #endif
  285. static char *EliminateCRLF( char *psz_string )
  286. {
  287.     char *p;
  288.     char *q;
  289.     for( p = psz_string; p && *p; p++ )
  290.     {
  291.         if( ( *p == 'r' ) && ( *(p+1) == 'n' ) )
  292.         {
  293.             for( q = p + 1; *q; q++ )
  294.                 *( q - 1 ) = *q;
  295.             *( q - 1 ) = '';
  296.         }
  297.     }
  298.     return psz_string;
  299. }
  300. // Convert UTF-8 string to UTF-16 character array -- internal Mac Endian-ness ;
  301. // we don't need to worry about bidirectional text conversion as ATSUI should
  302. // handle that for us automatically
  303. static void ConvertToUTF16( const char *psz_utf8_str, uint32_t *pi_strlen, UniChar **ppsz_utf16_str )
  304. {
  305.     CFStringRef   p_cfString;
  306.     int           i_string_length;
  307.     p_cfString = CFStringCreateWithCString( NULL, psz_utf8_str, kCFStringEncodingUTF8 );
  308.     if( !p_cfString )
  309.         return;
  310.     i_string_length = CFStringGetLength( p_cfString );
  311.     if( pi_strlen )
  312.         *pi_strlen = i_string_length;
  313.     if( !*ppsz_utf16_str )
  314.         *ppsz_utf16_str = (UniChar *) calloc( i_string_length, sizeof( UniChar ) );
  315.     CFStringGetCharacters( p_cfString, CFRangeMake( 0, i_string_length ), *ppsz_utf16_str );
  316.     CFRelease( p_cfString );
  317. }
  318. // Renders a text subpicture region into another one.
  319. // It is used as pf_add_string callback in the vout method by this module
  320. static int RenderText( filter_t *p_filter, subpicture_region_t *p_region_out,
  321.                        subpicture_region_t *p_region_in )
  322. {
  323.     filter_sys_t *p_sys = p_filter->p_sys;
  324.     UniChar      *psz_utf16_str = NULL;
  325.     uint32_t      i_string_length;
  326.     char         *psz_string;
  327.     int           i_font_color, i_font_alpha, i_font_size;
  328.     vlc_value_t val;
  329.     int i_scale = 1000;
  330.     p_sys->i_font_size    = GetFontSize( p_filter );
  331.     // Sanity check
  332.     if( !p_region_in || !p_region_out ) return VLC_EGENERIC;
  333.     psz_string = p_region_in->psz_text;
  334.     if( !psz_string || !*psz_string ) return VLC_EGENERIC;
  335.     if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
  336.         i_scale = val.i_int;
  337.     if( p_region_in->p_style )
  338.     {
  339.         i_font_color = __MAX( __MIN( p_region_in->p_style->i_font_color, 0xFFFFFF ), 0 );
  340.         i_font_alpha = __MAX( __MIN( p_region_in->p_style->i_font_alpha, 255 ), 0 );
  341.         i_font_size  = __MAX( __MIN( p_region_in->p_style->i_font_size, 255 ), 0 ) * i_scale / 1000;
  342.     }
  343.     else
  344.     {
  345.         i_font_color = p_sys->i_font_color;
  346.         i_font_alpha = 255 - p_sys->i_font_opacity;
  347.         i_font_size  = p_sys->i_font_size;
  348.     }
  349.     if( !i_font_alpha ) i_font_alpha = 255 - p_sys->i_font_opacity;
  350.     if( i_font_size <= 0 )
  351.     {
  352.         msg_Warn( p_filter, "invalid fontsize, using 12" );
  353.         if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
  354.             i_font_size = 12 * val.i_int / 1000;
  355.         else
  356.             i_font_size = 12;
  357.     }
  358.     ConvertToUTF16( EliminateCRLF( psz_string ), &i_string_length, &psz_utf16_str );
  359.     p_region_out->i_x = p_region_in->i_x;
  360.     p_region_out->i_y = p_region_in->i_y;
  361.     if( psz_utf16_str != NULL )
  362.     {
  363.         ATSUStyle p_style = CreateStyle( p_sys->psz_font_name, i_font_size,
  364.                                          (i_font_color & 0xffffff) |
  365.                                          ((i_font_alpha & 0xff) << 24),
  366.                                          false, false, false );
  367.         if( p_style )
  368.         {
  369.             RenderYUVA( p_filter, p_region_out, psz_utf16_str, i_string_length,
  370.                         1, &i_string_length, &p_style );
  371.         }
  372.         ATSUDisposeStyle( p_style );
  373.         free( psz_utf16_str );
  374.     }
  375.     return VLC_SUCCESS;
  376. }
  377. static ATSUStyle CreateStyle( char *psz_fontname, int i_font_size, uint32_t i_font_color,
  378.                               bool b_bold, bool b_italic, bool b_uline )
  379. {
  380.     ATSUStyle   p_style;
  381.     OSStatus    status;
  382.     uint32_t    i_tag_cnt;
  383.     float f_red   = (float)(( i_font_color & 0x00FF0000 ) >> 16) / 255.0;
  384.     float f_green = (float)(( i_font_color & 0x0000FF00 ) >>  8) / 255.0;
  385.     float f_blue  = (float)(  i_font_color & 0x000000FF        ) / 255.0;
  386.     float f_alpha = ( 255.0 - (float)(( i_font_color & 0xFF000000 ) >> 24)) / 255.0;
  387.     ATSUFontID           font;
  388.     Fixed                font_size  = IntToFixed( i_font_size );
  389.     ATSURGBAlphaColor    font_color = { f_red, f_green, f_blue, f_alpha };
  390.     Boolean              bold       = b_bold;
  391.     Boolean              italic     = b_italic;
  392.     Boolean              uline      = b_uline;
  393.     ATSUAttributeTag tags[]        = { kATSUSizeTag, kATSURGBAlphaColorTag, kATSUQDItalicTag,
  394.                                        kATSUQDBoldfaceTag, kATSUQDUnderlineTag, kATSUFontTag };
  395.     ByteCount sizes[]              = { sizeof( Fixed ), sizeof( ATSURGBAlphaColor ), sizeof( Boolean ),
  396.                                        sizeof( Boolean ), sizeof( Boolean ), sizeof( ATSUFontID )};
  397.     ATSUAttributeValuePtr values[] = { &font_size, &font_color, &italic, &bold, &uline, &font };
  398.     i_tag_cnt = sizeof( tags ) / sizeof( ATSUAttributeTag );
  399.     status = ATSUFindFontFromName( psz_fontname,
  400.                                    strlen( psz_fontname ),
  401.                                    kFontFullName,
  402.                                    kFontNoPlatform,
  403.                                    kFontNoScript,
  404.                                    kFontNoLanguageCode,
  405.                                    &font );
  406.     if( status != noErr )
  407.     {
  408.         // If we can't find a suitable font, just do everything else
  409.         i_tag_cnt--;
  410.     }
  411.     if( noErr == ATSUCreateStyle( &p_style ) )
  412.     {
  413.         if( noErr == ATSUSetAttributes( p_style, i_tag_cnt, tags, sizes, values ) )
  414.         {
  415.             return p_style;
  416.         }
  417.         ATSUDisposeStyle( p_style );
  418.     }
  419.     return NULL;
  420. }
  421. static ATSUStyle GetStyleFromFontStack( filter_sys_t *p_sys,
  422.         font_stack_t **p_fonts, bool b_bold, bool b_italic,
  423.         bool b_uline )
  424. {
  425.     ATSUStyle   p_style = NULL;
  426.     char     *psz_fontname = NULL;
  427.     uint32_t  i_font_color = p_sys->i_font_color;
  428.     uint32_t  i_karaoke_bg_color = i_font_color; /* Use it */
  429.     int       i_font_size  = p_sys->i_font_size;
  430.     if( VLC_SUCCESS == PeekFont( p_fonts, &psz_fontname, &i_font_size,
  431.                                  &i_font_color, &i_karaoke_bg_color ))
  432.     {
  433.         p_style = CreateStyle( psz_fontname, i_font_size, i_font_color,
  434.                                b_bold, b_italic, b_uline );
  435.     }
  436.     return p_style;
  437. }
  438. static void SetupLine( filter_t *p_filter, const char *psz_text_in,
  439.                        UniChar **psz_text_out, uint32_t *pi_runs,
  440.                        uint32_t **ppi_run_lengths, ATSUStyle **ppp_styles,
  441.                        ATSUStyle p_style )
  442. {
  443.     uint32_t i_string_length = 0;
  444.     ConvertToUTF16( psz_text_in, &i_string_length, psz_text_out );
  445.     *psz_text_out += i_string_length;
  446.     if( ppp_styles && ppi_run_lengths )
  447.     {
  448.         (*pi_runs)++;
  449.         if( *ppp_styles )
  450.             *ppp_styles = (ATSUStyle *) realloc( *ppp_styles, *pi_runs * sizeof( ATSUStyle ) );
  451.         else
  452.             *ppp_styles = (ATSUStyle *) malloc( *pi_runs * sizeof( ATSUStyle ) );
  453.         (*ppp_styles)[ *pi_runs - 1 ] = p_style;
  454.         if( *ppi_run_lengths )
  455.             *ppi_run_lengths = (uint32_t *) realloc( *ppi_run_lengths, *pi_runs * sizeof( uint32_t ) );
  456.         else
  457.             *ppi_run_lengths = (uint32_t *) malloc( *pi_runs * sizeof( uint32_t ) );
  458.         (*ppi_run_lengths)[ *pi_runs - 1 ] = i_string_length;
  459.     }
  460. }
  461. static int RenderHtml( filter_t *p_filter, subpicture_region_t *p_region_out,
  462.                        subpicture_region_t *p_region_in )
  463. {
  464.     int          rv = VLC_SUCCESS;
  465.     stream_t     *p_sub = NULL;
  466.     xml_t        *p_xml = NULL;
  467.     xml_reader_t *p_xml_reader = NULL;
  468.     if( !p_region_in || !p_region_in->psz_html )
  469.         return VLC_EGENERIC;
  470.     /* Reset the default fontsize in case screen metrics have changed */
  471.     p_filter->p_sys->i_font_size = GetFontSize( p_filter );
  472.     p_sub = stream_MemoryNew( VLC_OBJECT(p_filter),
  473.                               (uint8_t *) p_region_in->psz_html,
  474.                               strlen( p_region_in->psz_html ),
  475.                               true );
  476.     if( p_sub )
  477.     {
  478.         p_xml = xml_Create( p_filter );
  479.         if( p_xml )
  480.         {
  481.             bool b_karaoke = false;
  482.             p_xml_reader = xml_ReaderCreate( p_xml, p_sub );
  483.             if( p_xml_reader )
  484.             {
  485.                 /* Look for Root Node */
  486.                 if( xml_ReaderRead( p_xml_reader ) == 1 )
  487.                 {
  488.                     char *psz_node = xml_ReaderName( p_xml_reader );
  489.                     if( !strcasecmp( "karaoke", psz_node ) )
  490.                     {
  491.                         /* We're going to have to render the text a number
  492.                          * of times to show the progress marker on the text.
  493.                          */
  494.                         var_SetBool( p_filter, "text-rerender", true );
  495.                         b_karaoke = true;
  496.                     }
  497.                     else if( !strcasecmp( "text", psz_node ) )
  498.                     {
  499.                         b_karaoke = false;
  500.                     }
  501.                     else
  502.                     {
  503.                         /* Only text and karaoke tags are supported */
  504.                         xml_ReaderDelete( p_xml, p_xml_reader );
  505.                         p_xml_reader = NULL;
  506.                         rv = VLC_EGENERIC;
  507.                     }
  508.                     free( psz_node );
  509.                 }
  510.             }
  511.             if( p_xml_reader )
  512.             {
  513.                 UniChar    *psz_text;
  514.                 int         i_len = 0;
  515.                 uint32_t    i_runs = 0;
  516.                 uint32_t    i_k_runs = 0;
  517.                 uint32_t   *pi_run_lengths = NULL;
  518.                 uint32_t   *pi_k_run_lengths = NULL;
  519.                 uint32_t   *pi_k_durations = NULL;
  520.                 ATSUStyle  *pp_styles = NULL;
  521.                 psz_text = (UniChar *) malloc( strlen( p_region_in->psz_html ) *
  522.                                                 sizeof( UniChar ) );
  523.                 if( psz_text )
  524.                 {
  525.                     uint32_t k;
  526.                     rv = ProcessNodes( p_filter, p_xml_reader,
  527.                                   p_region_in->p_style, psz_text, &i_len,
  528.                                   &i_runs, &pi_run_lengths, &pp_styles,
  529.                                   /* No karaoke support */
  530.                                   false, &i_k_runs, &pi_k_run_lengths, &pi_k_durations );
  531.                     assert( pi_k_run_lengths == NULL && pi_k_durations == NULL );
  532.                     p_region_out->i_x = p_region_in->i_x;
  533.                     p_region_out->i_y = p_region_in->i_y;
  534.                     if(( rv == VLC_SUCCESS ) && ( i_len > 0 ))
  535.                     {
  536.                         RenderYUVA( p_filter, p_region_out, psz_text, i_len, i_runs,
  537.                              pi_run_lengths, pp_styles);
  538.                     }
  539.                     for( k=0; k<i_runs; k++)
  540.                         ATSUDisposeStyle( pp_styles[k] );
  541.                     free( pp_styles );
  542.                     free( pi_run_lengths );
  543.                     free( psz_text );
  544.                 }
  545.                 xml_ReaderDelete( p_xml, p_xml_reader );
  546.             }
  547.             xml_Delete( p_xml );
  548.         }
  549.         stream_Delete( p_sub );
  550.     }
  551.     return rv;
  552. }
  553. static CGContextRef CreateOffScreenContext( int i_width, int i_height,
  554.                          offscreen_bitmap_t **pp_memory, CGColorSpaceRef *pp_colorSpace )
  555. {
  556.     offscreen_bitmap_t *p_bitmap;
  557.     CGContextRef        p_context = NULL;
  558.     p_bitmap = (offscreen_bitmap_t *) malloc( sizeof( offscreen_bitmap_t ));
  559.     if( p_bitmap )
  560.     {
  561.         p_bitmap->i_bitsPerChannel = 8;
  562.         p_bitmap->i_bitsPerPixel   = 4 * p_bitmap->i_bitsPerChannel; // A,R,G,B
  563.         p_bitmap->i_bytesPerPixel  = p_bitmap->i_bitsPerPixel / 8;
  564.         p_bitmap->i_bytesPerRow    = i_width * p_bitmap->i_bytesPerPixel;
  565.         p_bitmap->p_data = calloc( i_height, p_bitmap->i_bytesPerRow );
  566.         *pp_colorSpace = CGColorSpaceCreateWithName( kCGColorSpaceGenericRGB );
  567.         if( p_bitmap->p_data && *pp_colorSpace )
  568.         {
  569.             p_context = CGBitmapContextCreate( p_bitmap->p_data, i_width, i_height,
  570.                                 p_bitmap->i_bitsPerChannel, p_bitmap->i_bytesPerRow,
  571.                                 *pp_colorSpace, kCGImageAlphaPremultipliedFirst);
  572.         }
  573.         if( p_context )
  574.         {
  575.             if( CGContextSetAllowsAntialiasing != NULL )
  576.             {
  577.                 CGContextSetAllowsAntialiasing( p_context, true );
  578.             }
  579.         }
  580.         *pp_memory = p_bitmap;
  581.     }
  582.     return p_context;
  583. }
  584. static offscreen_bitmap_t *Compose( int i_text_align, UniChar *psz_utf16_str, uint32_t i_text_len,
  585.                                     uint32_t i_runs, uint32_t *pi_run_lengths, ATSUStyle *pp_styles,
  586.                                     int i_width, int i_height, int *pi_textblock_height )
  587. {
  588.     offscreen_bitmap_t  *p_offScreen  = NULL;
  589.     CGColorSpaceRef      p_colorSpace = NULL;
  590.     CGContextRef         p_context = NULL;
  591.     p_context = CreateOffScreenContext( i_width, i_height, &p_offScreen, &p_colorSpace );
  592.     if( p_context )
  593.     {
  594.         ATSUTextLayout p_textLayout;
  595.         OSStatus status = noErr;
  596.         status = ATSUCreateTextLayoutWithTextPtr( psz_utf16_str, 0, i_text_len, i_text_len,
  597.                                                   i_runs,
  598.                                                   (const UniCharCount *) pi_run_lengths,
  599.                                                   pp_styles,
  600.                                                   &p_textLayout );
  601.         if( status == noErr )
  602.         {
  603.             // Attach our offscreen Image Graphics Context to the text style
  604.             // and setup the line alignment (have to specify the line width
  605.             // also in order for our chosen alignment to work)
  606.             Fract   alignment  = kATSUStartAlignment;
  607.             Fixed   line_width = Long2Fix( i_width - HORIZONTAL_MARGIN * 2 );
  608.             ATSUAttributeTag tags[]        = { kATSUCGContextTag, kATSULineFlushFactorTag, kATSULineWidthTag };
  609.             ByteCount sizes[]              = { sizeof( CGContextRef ), sizeof( Fract ), sizeof( Fixed ) };
  610.             ATSUAttributeValuePtr values[] = { &p_context, &alignment, &line_width };
  611.             int i_tag_cnt = sizeof( tags ) / sizeof( ATSUAttributeTag );
  612.             if( i_text_align == SUBPICTURE_ALIGN_RIGHT )
  613.             {
  614.                 alignment = kATSUEndAlignment;
  615.             }
  616.             else if( i_text_align != SUBPICTURE_ALIGN_LEFT )
  617.             {
  618.                 alignment = kATSUCenterAlignment;
  619.             }
  620.             ATSUSetLayoutControls( p_textLayout, i_tag_cnt, tags, sizes, values );
  621.             // let ATSUI deal with characters not-in-our-specified-font
  622.             ATSUSetTransientFontMatching( p_textLayout, true );
  623.             Fixed x = Long2Fix( HORIZONTAL_MARGIN );
  624.             Fixed y = Long2Fix( i_height );
  625.             // Set the line-breaks and draw individual lines
  626.             uint32_t i_start = 0;
  627.             uint32_t i_end = i_text_len;
  628.             // Set up black outlining of the text --
  629.             CGContextSetRGBStrokeColor( p_context, 0, 0, 0, 0.5 );
  630.             CGContextSetTextDrawingMode( p_context, kCGTextFillStroke );
  631.             CGContextSetShadow( p_context, CGSizeMake( 0, 0 ), 5 );
  632.             float black_components[4] = {0, 0, 0, 1};
  633.             CGColorRef outlinecolor = CGColorCreate( CGColorSpaceCreateWithName( kCGColorSpaceGenericRGB ), black_components );
  634.             CGContextSetShadowWithColor (p_context, CGSizeMake( 0, 0 ), 5, outlinecolor);
  635.             CGColorRelease( outlinecolor );
  636.             do
  637.             {
  638.                 // ATSUBreakLine will automatically pick up any manual 'n's also
  639.                 status = ATSUBreakLine( p_textLayout, i_start, line_width, true, (UniCharArrayOffset *) &i_end );
  640.                 if( ( status == noErr ) || ( status == kATSULineBreakInWord ) )
  641.                 {
  642.                     Fixed     ascent;
  643.                     Fixed     descent;
  644.                     uint32_t  i_actualSize;
  645.                     // Come down far enough to fit the height of this line --
  646.                     ATSUGetLineControl( p_textLayout, i_start, kATSULineAscentTag,
  647.                                     sizeof( Fixed ), &ascent, (ByteCount *) &i_actualSize );
  648.                     // Quartz uses an upside-down co-ordinate space -> y values decrease as
  649.                     // you move down the page
  650.                     y -= ascent;
  651.                     // Set the outlining for this line to be dependent on the size of the line -
  652.                     // make it about 5% of the ascent, with a minimum at 1.0
  653.                     float f_thickness = FixedToFloat( ascent ) * 0.05;
  654.                     CGContextSetLineWidth( p_context, (( f_thickness < 1.0 ) ? 1.0 : f_thickness ));
  655.                     ATSUDrawText( p_textLayout, i_start, i_end - i_start, x, y );
  656.                     // and now prepare for the next line by coming down far enough for our
  657.                     // descent
  658.                     ATSUGetLineControl( p_textLayout, i_start, kATSULineDescentTag,
  659.                                     sizeof( Fixed ), &descent, (ByteCount *) &i_actualSize );
  660.                     y -= descent;
  661.                     i_start = i_end;
  662.                 }
  663.                 else
  664.                     break;
  665.             }
  666.             while( i_end < i_text_len );
  667.             *pi_textblock_height = i_height - Fix2Long( y );
  668.             CGContextFlush( p_context );
  669.             ATSUDisposeTextLayout( p_textLayout );
  670.         }
  671.         CGContextRelease( p_context );
  672.     }
  673.     if( p_colorSpace ) CGColorSpaceRelease( p_colorSpace );
  674.     return p_offScreen;
  675. }
  676. static int GetFontSize( filter_t *p_filter )
  677. {
  678.     return p_filter->fmt_out.video.i_height / __MAX(1, var_CreateGetInteger( p_filter, "quartztext-rel-fontsize" ));
  679. }
  680. static int RenderYUVA( filter_t *p_filter, subpicture_region_t *p_region, UniChar *psz_utf16_str,
  681.                        uint32_t i_text_len, uint32_t i_runs, uint32_t *pi_run_lengths, ATSUStyle *pp_styles )
  682. {
  683.     offscreen_bitmap_t *p_offScreen = NULL;
  684.     int      i_textblock_height = 0;
  685.     int i_width = p_filter->fmt_out.video.i_visible_width;
  686.     int i_height = p_filter->fmt_out.video.i_visible_height;
  687.     int i_text_align = p_region->i_align & 0x3;
  688.     if( !psz_utf16_str )
  689.     {
  690.         msg_Err( p_filter, "Invalid argument to RenderYUVA" );
  691.         return VLC_EGENERIC;
  692.     }
  693.     p_offScreen = Compose( i_text_align, psz_utf16_str, i_text_len,
  694.                            i_runs, pi_run_lengths, pp_styles,
  695.                            i_width, i_height, &i_textblock_height );
  696.     if( !p_offScreen )
  697.     {
  698.         msg_Err( p_filter, "No offscreen buffer" );
  699.         return VLC_EGENERIC;
  700.     }
  701.     uint8_t *p_dst_y,*p_dst_u,*p_dst_v,*p_dst_a;
  702.     video_format_t fmt;
  703.     int x, y, i_offset, i_pitch;
  704.     uint8_t i_y, i_u, i_v; // YUV values, derived from incoming RGB
  705.     // Create a new subpicture region
  706.     memset( &fmt, 0, sizeof(video_format_t) );
  707.     fmt.i_chroma = VLC_FOURCC('Y','U','V','A');
  708.     fmt.i_aspect = 0;
  709.     fmt.i_width = fmt.i_visible_width = i_width;
  710.     fmt.i_height = fmt.i_visible_height = __MIN( i_height, i_textblock_height + VERTICAL_MARGIN * 2);
  711.     fmt.i_x_offset = fmt.i_y_offset = 0;
  712.     p_region->p_picture = picture_New( fmt.i_chroma, fmt.i_width, fmt.i_height, fmt.i_aspect );
  713.     if( !p_region->p_picture )
  714.         return VLC_EGENERIC;
  715.     p_region->fmt = fmt;
  716.     p_dst_y = p_region->p_picture->Y_PIXELS;
  717.     p_dst_u = p_region->p_picture->U_PIXELS;
  718.     p_dst_v = p_region->p_picture->V_PIXELS;
  719.     p_dst_a = p_region->p_picture->A_PIXELS;
  720.     i_pitch = p_region->p_picture->A_PITCH;
  721.     i_offset = (i_height+VERTICAL_MARGIN < fmt.i_height) ? VERTICAL_MARGIN *i_pitch : 0 ;
  722.     for( y=0; y<fmt.i_height; y++)
  723.     {
  724.         for( x=0; x<fmt.i_width; x++)
  725.         {
  726.             int i_alpha = p_offScreen->p_data[ y * p_offScreen->i_bytesPerRow + x * p_offScreen->i_bytesPerPixel     ];
  727.             int i_red   = p_offScreen->p_data[ y * p_offScreen->i_bytesPerRow + x * p_offScreen->i_bytesPerPixel + 1 ];
  728.             int i_green = p_offScreen->p_data[ y * p_offScreen->i_bytesPerRow + x * p_offScreen->i_bytesPerPixel + 2 ];
  729.             int i_blue  = p_offScreen->p_data[ y * p_offScreen->i_bytesPerRow + x * p_offScreen->i_bytesPerPixel + 3 ];
  730.             i_y = (uint8_t)__MIN(abs( 2104 * i_red  + 4130 * i_green +
  731.                               802 * i_blue + 4096 + 131072 ) >> 13, 235);
  732.             i_u = (uint8_t)__MIN(abs( -1214 * i_red  + -2384 * i_green +
  733.                              3598 * i_blue + 4096 + 1048576) >> 13, 240);
  734.             i_v = (uint8_t)__MIN(abs( 3598 * i_red + -3013 * i_green +
  735.                               -585 * i_blue + 4096 + 1048576) >> 13, 240);
  736.             p_dst_y[ i_offset + x ] = i_y;
  737.             p_dst_u[ i_offset + x ] = i_u;
  738.             p_dst_v[ i_offset + x ] = i_v;
  739.             p_dst_a[ i_offset + x ] = i_alpha;
  740.         }
  741.         i_offset += i_pitch;
  742.     }
  743.     free( p_offScreen->p_data );
  744.     free( p_offScreen );
  745.     return VLC_SUCCESS;
  746. }
  747. #endif