+
+/*
+ This retrieves the size of the video.
+ The best case is when the original video size is known, as we can then
+ scale images to match. In this case, since VLC autoscales, we want to
+ return the original size and let VLC scale everything.
+ if the original size is not known, then VLC can't resize, so we return
+ the size of the incoming video. If sizes in the Kate stream are in
+ relative units, it works fine. If they are absolute, you get what you
+ ask for. Images aren't rescaled.
+*/
+static void GetVideoSize( decoder_t *p_dec, int *w, int *h )
+{
+ /* searching for vout to get its size is frowned upon, so we don't and
+ use a default size if the original canvas size is not specified. */
+#if 1
+ decoder_sys_t *p_sys = p_dec->p_sys;
+ if( p_sys->ki.original_canvas_width > 0 && p_sys->ki.original_canvas_height > 0 )
+ {
+ *w = p_sys->ki.original_canvas_width;
+ *h = p_sys->ki.original_canvas_height;
+ msg_Dbg( p_dec, "original canvas %zu %zu",
+ p_sys->ki.original_canvas_width, p_sys->ki.original_canvas_height );
+ }
+ else
+ {
+ /* nothing, leave defaults */
+ msg_Dbg( p_dec, "original canvas size unknown");
+ }
+#else
+ /* keep this just in case it might be allowed one day ;) */
+ vout_thread_t *p_vout;
+ p_vout = vlc_object_find( (vlc_object_t*)p_dec, VLC_OBJECT_VOUT, FIND_CHILD );
+ if( p_vout )
+ {
+ decoder_sys_t *p_sys = p_dec->p_sys;
+ if( p_sys->ki.original_canvas_width > 0 && p_sys->ki.original_canvas_height > 0 )
+ {
+ *w = p_sys->ki.original_canvas_width;
+ *h = p_sys->ki.original_canvas_height;
+ }
+ else
+ {
+ *w = p_vout->fmt_in.i_width;
+ *h = p_vout->fmt_in.i_height;
+ }
+ msg_Dbg( p_dec, "video: in %d %d, out %d %d, original canvas %zu %zu",
+ p_vout->fmt_in.i_width, p_vout->fmt_in.i_height,
+ p_vout->fmt_out.i_width, p_vout->fmt_out.i_height,
+ p_sys->ki.original_canvas_width, p_sys->ki.original_canvas_height );
+ vlc_object_release( p_vout );
+ }
+#endif
+}
+
+static void CreateKateBitmap( picture_t *pic, const kate_bitmap *bitmap )
+{
+ size_t y;
+
+ for( y=0; y<bitmap->height; ++y )
+ {
+ uint8_t *dest = pic->Y_PIXELS+pic->Y_PITCH*y;
+ const uint8_t *src = bitmap->pixels+y*bitmap->width;
+ memcpy( dest, src, bitmap->width );
+ }
+}
+
+static void CreateKatePalette( video_palette_t *fmt_palette, const kate_palette *palette )
+{
+ size_t n;
+
+ fmt_palette->i_entries = palette->ncolors;
+ for( n=0; n<palette->ncolors; ++n )
+ {
+ rgb_to_yuv(
+ &fmt_palette->palette[n][0], &fmt_palette->palette[n][1], &fmt_palette->palette[n][2],
+ palette->colors[n].r, palette->colors[n].g, palette->colors[n].b
+ );
+ fmt_palette->palette[n][3] = palette->colors[n].a;
+ }
+}
+
+static void SetupText( decoder_t *p_dec, subpicture_t *p_spu, const kate_event *ev )
+{
+ decoder_sys_t *p_sys = p_dec->p_sys;
+
+ if( ev->text_encoding != kate_utf8 )
+ {
+ msg_Warn( p_dec, "Text isn't UTF-8, unsupported, ignored" );
+ return;
+ }
+
+ switch( ev->text_markup_type )
+ {
+ case kate_markup_none:
+ p_spu->p_region->psz_text = strdup( ev->text ); /* no leak, this actually gets killed by the core */
+ break;
+ case kate_markup_simple:
+ if( p_sys->b_formatted )
+ {
+ /* the HTML renderer expects a top level text tag pair */
+ char *buffer = NULL;
+ if( asprintf( &buffer, "<text>%s</text>", ev->text ) >= 0 )
+ {
+ p_spu->p_region->psz_html = buffer;
+ }
+ break;
+ }
+ /* if not formatted, we fall through */
+ default:
+ /* we don't know about this one, so remove markup and display as text */
+ {
+ char *copy = strdup( ev->text );
+ size_t len0 = strlen( copy ) + 1;
+ kate_text_remove_markup( ev->text_encoding, copy, &len0 );
+ p_spu->p_region->psz_text = copy;
+ }
+ break;
+ }
+}
+
+#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;
+}
+