+#ifdef HAVE_TIGER
+
+static void TigerDestroySubpicture( subpicture_t *p_subpic )
+{
+ DecSysRelease( p_subpic->updater.p_sys->p_dec_sys );
+ free( p_subpic->updater.p_sys );
+}
+/*
+ * We get premultiplied alpha, but VLC doesn't expect this, so we demultiply
+ * alpha to avoid double multiply (and thus thinner text than we should)).
+ * Best would be to have VLC be able to handle premultiplied alpha, as it
+ * would be faster to blend as well.
+ *
+ * Also, we flip color components around for big endian machines, as Tiger
+ * outputs ARGB or ABGR (the one we selected here) in host endianness.
+ */
+static void PostprocessTigerImage( plane_t *p_plane, unsigned int i_width )
+{
+ PROFILE_START( tiger_renderer_postprocess );
+ int y;
+ for( y=0; y<p_plane->i_lines; ++y )
+ {
+ uint8_t *p_line = (uint8_t*)(p_plane->p_pixels + y*p_plane->i_pitch);
+ unsigned int x;
+ for( x=0; x<i_width; ++x )
+ {
+ uint8_t *p_pixel = p_line+x*4;
+#ifdef WORDS_BIGENDIAN
+ uint8_t a = p_pixel[0];
+#else
+ uint8_t a = p_pixel[3];
+#endif
+ if( a )
+ {
+#ifdef WORDS_BIGENDIAN
+ uint8_t tmp = p_pixel[2];
+ p_pixel[0] = clip_uint8_vlc((p_pixel[3] * 255 + a / 2) / a);
+ p_pixel[3] = a;
+ p_pixel[2] = clip_uint8_vlc((p_pixel[1] * 255 + a / 2) / a);
+ p_pixel[1] = clip_uint8_vlc((tmp * 255 + a / 2) / a);
+#else
+ p_pixel[0] = clip_uint8_vlc((p_pixel[0] * 255 + a / 2) / a);
+ p_pixel[1] = clip_uint8_vlc((p_pixel[1] * 255 + a / 2) / a);
+ p_pixel[2] = clip_uint8_vlc((p_pixel[2] * 255 + a / 2) / a);
+#endif
+ }
+ else
+ {
+ p_pixel[0] = 0;
+ p_pixel[1] = 0;
+ p_pixel[2] = 0;
+ p_pixel[3] = 0;
+ }
+ }
+ }
+ PROFILE_STOP( tiger_renderer_postprocess );
+}
+
+static int TigerValidateSubpicture( subpicture_t *p_subpic,
+ bool b_fmt_src, const video_format_t *p_fmt_src,
+ bool b_fmt_dst, const video_format_t *p_fmt_dst,
+ mtime_t ts )
+{
+ decoder_sys_t *p_sys = p_subpic->updater.p_sys->p_dec_sys;
+
+ if( b_fmt_src || b_fmt_dst )
+ return VLC_EGENERIC;
+
+ PROFILE_START( TigerValidateSubpicture );
+
+ /* time in seconds from the start of the stream */
+ kate_float t = (p_subpic->updater.p_sys->i_start + ts - p_subpic->i_start ) / 1000000.0f;
+
+ /* it is likely that the current region (if any) can be kept as is; test for this */
+ vlc_mutex_lock( &p_sys->lock );
+ int i_ret;
+ if( p_sys->b_dirty || tiger_renderer_is_dirty( p_sys->p_tr ) )
+ {
+ i_ret = VLC_EGENERIC;
+ goto exit;
+ }
+ if( tiger_renderer_update( p_sys->p_tr, t, 1 ) >= 0 &&
+ tiger_renderer_is_dirty( p_sys->p_tr ) )
+ {
+ i_ret = VLC_EGENERIC;
+ goto exit;
+ }
+
+ i_ret = VLC_SUCCESS;
+exit:
+ vlc_mutex_unlock( &p_sys->lock );
+ PROFILE_STOP( TigerValidateSubpicture );
+ return i_ret;
+}
+
+/* Tiger renders can end up looking a bit crap since they get overlaid on top of
+ a subsampled YUV image, so there can be a fair amount of chroma bleeding.
+ Looks good with white though since it's all luma. Hopefully that will be the
+ common case. */
+static void TigerUpdateSubpicture( subpicture_t *p_subpic,
+ const video_format_t *p_fmt_src,
+ const video_format_t *p_fmt_dst,
+ mtime_t ts )
+{
+ decoder_sys_t *p_sys = p_subpic->updater.p_sys->p_dec_sys;
+ plane_t *p_plane;
+ kate_float t;
+ int i_ret;
+
+
+ /* time in seconds from the start of the stream */
+ t = (p_subpic->updater.p_sys->i_start + ts - p_subpic->i_start ) / 1000000.0f;
+
+ PROFILE_START( TigerUpdateSubpicture );
+
+ /* create a full frame region - this will also tell Tiger the size of the frame */
+ video_format_t fmt = *p_fmt_dst;
+ fmt.i_chroma = VLC_CODEC_RGBA;
+ fmt.i_bits_per_pixel = 0;
+ fmt.i_width =
+ fmt.i_visible_width = p_fmt_src->i_width;
+ fmt.i_height =
+ fmt.i_visible_height = p_fmt_src->i_height;
+ fmt.i_x_offset = fmt.i_y_offset = 0;
+
+ subpicture_region_t *p_r = subpicture_region_New( &fmt );
+ if( !p_r )
+ return;
+
+ p_r->i_x = 0;
+ p_r->i_y = 0;
+ p_r->i_align = SUBPICTURE_ALIGN_TOP | SUBPICTURE_ALIGN_LEFT;
+
+ vlc_mutex_lock( &p_sys->lock );
+
+ p_plane = &p_r->p_picture->p[0];
+ i_ret = tiger_renderer_set_buffer( p_sys->p_tr, p_plane->p_pixels, fmt.i_width, p_plane->i_lines, p_plane->i_pitch, 1 );
+ if( i_ret < 0 )
+ {
+ goto failure;
+ }
+
+ PROFILE_START( tiger_renderer_update );
+ i_ret = tiger_renderer_update( p_sys->p_tr, t, 1 );
+ if( i_ret < 0 )
+ {
+ goto failure;
+ }
+ PROFILE_STOP( tiger_renderer_update );
+
+ PROFILE_START( tiger_renderer_render );
+ i_ret = tiger_renderer_render( p_sys->p_tr );
+ if( i_ret < 0 )
+ {
+ goto failure;
+ }
+ PROFILE_STOP( tiger_renderer_render );
+
+ PostprocessTigerImage( p_plane, fmt.i_width );
+ p_subpic->p_region = p_r;
+ p_sys->b_dirty = false;
+
+ PROFILE_STOP( TigerUpdateSubpicture );
+
+ vlc_mutex_unlock( &p_sys->lock );
+
+ return;
+
+failure:
+ vlc_mutex_unlock( &p_sys->lock );
+ subpicture_region_ChainDelete( p_r );
+}
+
+static uint32_t GetTigerColor( decoder_t *p_dec, const char *psz_prefix )
+{
+ char *psz_tmp;
+ uint32_t i_color = 0;
+
+ if( asprintf( &psz_tmp, "%s-color", psz_prefix ) >= 0 )
+ {
+ uint32_t i_rgb = var_CreateGetInteger( p_dec, psz_tmp );
+ var_Destroy( p_dec, psz_tmp );
+ free( psz_tmp );
+ i_color |= i_rgb;
+ }
+
+ if( asprintf( &psz_tmp, "%s-alpha", psz_prefix ) >= 0 )
+ {
+ uint32_t i_alpha = var_CreateGetInteger( p_dec, psz_tmp );
+ var_Destroy( p_dec, psz_tmp );
+ free( psz_tmp );
+ i_color |= (i_alpha << 24);
+ }
+
+ return i_color;
+}
+
+static char *GetTigerString( decoder_t *p_dec, const char *psz_name )
+{
+ char *psz_value = var_CreateGetString( p_dec, psz_name );
+ if( psz_value)
+ {
+ psz_value = strdup( psz_value );
+ }
+ var_Destroy( p_dec, psz_name );
+ return psz_value;
+}
+
+static int GetTigerInteger( decoder_t *p_dec, const char *psz_name )
+{
+ int i_value = var_CreateGetInteger( p_dec, psz_name );
+ var_Destroy( p_dec, psz_name );
+ return i_value;
+}
+
+static double GetTigerFloat( decoder_t *p_dec, const char *psz_name )
+{
+ double f_value = var_CreateGetFloat( p_dec, psz_name );
+ var_Destroy( p_dec, psz_name );
+ return f_value;
+}
+
+static void UpdateTigerQuality( decoder_t *p_dec )
+{
+ decoder_sys_t *p_sys = (decoder_sys_t*)p_dec->p_sys;
+ CHECK_TIGER_RET( tiger_renderer_set_quality( p_sys->p_tr, p_sys->f_tiger_quality ) );
+ p_sys->b_dirty = true;
+}
+
+static void UpdateTigerFontDesc( decoder_t *p_dec )
+{
+ decoder_sys_t *p_sys = (decoder_sys_t*)p_dec->p_sys;
+ CHECK_TIGER_RET( tiger_renderer_set_default_font_description( p_sys->p_tr, p_sys->psz_tiger_default_font_desc ) );
+ p_sys->b_dirty = true;
+}
+
+static void UpdateTigerFontColor( decoder_t *p_dec )
+{
+ decoder_sys_t *p_sys = (decoder_sys_t*)p_dec->p_sys;
+ double f_a = ((p_sys->i_tiger_default_font_color >> 24) & 0xff) / 255.0;
+ double f_r = ((p_sys->i_tiger_default_font_color >> 16) & 0xff) / 255.0;
+ double f_g = ((p_sys->i_tiger_default_font_color >> 8) & 0xff) / 255.0;
+ double f_b = (p_sys->i_tiger_default_font_color & 0xff) / 255.0;
+ CHECK_TIGER_RET( tiger_renderer_set_default_font_color( p_sys->p_tr, f_r, f_g, f_b, f_a ) );
+ p_sys->b_dirty = true;
+}
+
+static void UpdateTigerBackgroundColor( decoder_t *p_dec )
+{
+ decoder_sys_t *p_sys = (decoder_sys_t*)p_dec->p_sys;
+ double f_a = ((p_sys->i_tiger_default_background_color >> 24) & 0xff) / 255.0;
+ double f_r = ((p_sys->i_tiger_default_background_color >> 16) & 0xff) / 255.0;
+ double f_g = ((p_sys->i_tiger_default_background_color >> 8) & 0xff) / 255.0;
+ double f_b = (p_sys->i_tiger_default_background_color & 0xff) / 255.0;
+ CHECK_TIGER_RET( tiger_renderer_set_default_background_fill_color( p_sys->p_tr, f_r, f_g, f_b, f_a ) );
+ p_sys->b_dirty = true;
+}
+
+static void UpdateTigerFontEffect( decoder_t *p_dec )
+{
+ decoder_sys_t *p_sys = (decoder_sys_t*)p_dec->p_sys;
+ CHECK_TIGER_RET( tiger_renderer_set_default_font_effect( p_sys->p_tr,
+ p_sys->e_tiger_default_font_effect,
+ p_sys->f_tiger_default_font_effect_strength ) );
+ p_sys->b_dirty = true;
+}
+
+static int OnConfigurationChanged( decoder_t *p_dec, const char *psz_var,
+ vlc_value_t oldval, vlc_value_t newval )
+{
+ decoder_sys_t *p_sys = (decoder_sys_t*)p_dec->p_sys;
+
+ VLC_UNUSED( oldval );
+
+ vlc_mutex_lock( &p_sys->lock );
+
+ msg_Dbg( p_dec, "OnConfigurationChanged: %s", psz_var );
+
+ if( !p_sys->b_use_tiger || !p_sys->p_tr )
+ {
+ vlc_mutex_unlock( &p_sys->lock );
+ return VLC_SUCCESS;
+ }
+
+#define TEST_TIGER_VAR( name ) \
+ if( !strcmp( name, psz_var ) )
+
+ TEST_TIGER_VAR( "kate-tiger-quality" )
+ {
+ p_sys->f_tiger_quality = newval.f_float;
+ UpdateTigerQuality( p_dec );
+ }
+
+ TEST_TIGER_VAR( "kate-tiger-default-font-desc" )
+ {
+ if( p_sys->psz_tiger_default_font_desc )
+ {
+ free( p_sys->psz_tiger_default_font_desc );
+ p_sys->psz_tiger_default_font_desc = NULL;
+ }
+ if( newval.psz_string )
+ {
+ p_sys->psz_tiger_default_font_desc = strdup( newval.psz_string );
+ }
+ UpdateTigerFontDesc( p_dec );
+ }
+
+ TEST_TIGER_VAR( "kate-tiger-default-font-color" )
+ {
+ p_sys->i_tiger_default_font_color = (p_sys->i_tiger_default_font_color & 0xff00000000) | newval.i_int;
+ UpdateTigerFontColor( p_dec );
+ }
+
+ TEST_TIGER_VAR( "kate-tiger-default-font-alpha" )
+ {
+ p_sys->i_tiger_default_font_color = (p_sys->i_tiger_default_font_color & 0x00ffffff) | (newval.i_int<<24);
+ UpdateTigerFontColor( p_dec );
+ }
+
+ TEST_TIGER_VAR( "kate-tiger-default-background-color" )
+ {
+ p_sys->i_tiger_default_background_color = (p_sys->i_tiger_default_background_color & 0xff00000000) | newval.i_int;
+ UpdateTigerBackgroundColor( p_dec );
+ }
+
+ TEST_TIGER_VAR( "kate-tiger-default-background-alpha" )
+ {
+ p_sys->i_tiger_default_background_color = (p_sys->i_tiger_default_background_color & 0x00ffffff) | (newval.i_int<<24);
+ UpdateTigerBackgroundColor( p_dec );
+ }
+
+ TEST_TIGER_VAR( "kate-tiger-default-font-effect" )
+ {
+ p_sys->e_tiger_default_font_effect = (tiger_font_effect)newval.i_int;
+ UpdateTigerFontEffect( p_dec );
+ }
+
+ TEST_TIGER_VAR( "kate-tiger-default-font-effect-strength" )
+ {
+ p_sys->f_tiger_default_font_effect_strength = newval.f_float;
+ UpdateTigerFontEffect( p_dec );
+ }
+
+#undef TEST_TIGER_VAR
+
+ vlc_mutex_unlock( &p_sys->lock );
+
+ return VLC_SUCCESS;
+}
+
+static int TigerConfigurationCallback( vlc_object_t *p_this, const char *psz_var,
+ vlc_value_t oldval, vlc_value_t newval,
+ void *p_data )
+{
+ size_t i_idx;
+
+ VLC_UNUSED( p_this );
+ VLC_UNUSED( oldval );
+ VLC_UNUSED( newval );
+ VLC_UNUSED( p_data );
+
+ vlc_mutex_lock( &kate_decoder_list_mutex );
+
+ /* Update all existing decoders from the global user prefs */
+ for( i_idx = 0; i_idx < kate_decoder_list_size; i_idx++ )
+ {
+ decoder_t *p_dec = kate_decoder_list[ i_idx ];
+ OnConfigurationChanged( p_dec, psz_var, oldval, newval );
+ }
+
+ vlc_mutex_unlock( &kate_decoder_list_mutex );
+
+ return VLC_SUCCESS;
+}
+
+#endif
+