demux:mkv: fix wrong value reset after clean of an array
[vlc.git] / modules / text_renderer / svg.c
blob5f1a9c62e206bdd367c3d7fe8b3c7669cc6fa3d8
1 /*****************************************************************************
2 * svg.c : Put SVG on the video
3 *****************************************************************************
4 * Copyright (C) 2002, 2003 VLC authors and VideoLAN
6 * Authors: Olivier Aubert <oaubert@lisi.univ-lyon1.fr>
8 * This program is free software; you can redistribute it and/or modify it
9 * under the terms of the GNU Lesser General Public License as published by
10 * the Free Software Foundation; either version 2.1 of the License, or
11 * (at your option ) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public License
19 * along with this program; if not, write to the Free Software Foundation,
20 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
21 *****************************************************************************/
23 /*****************************************************************************
24 * Preamble
25 *****************************************************************************/
27 #ifdef HAVE_CONFIG_H
28 # include "config.h"
29 #endif
31 #include <vlc_common.h>
32 #include <vlc_plugin.h>
33 #include <vlc_fs.h>
34 #include <vlc_filter.h>
35 #include <vlc_subpicture.h>
36 #include <vlc_strings.h>
38 #include <sys/types.h>
39 #include <unistd.h>
41 #include <glib.h>
42 #include <glib/gstdio.h>
43 #include <glib-object.h> /* g_object_unref( ) */
44 #include <librsvg/rsvg.h>
45 #include <cairo/cairo.h>
47 /*****************************************************************************
48 * Local prototypes
49 *****************************************************************************/
50 static int Create ( vlc_object_t * );
51 static void Destroy ( vlc_object_t * );
52 static int RenderText( filter_t *p_filter, subpicture_region_t *p_region_out,
53 subpicture_region_t *p_region_in,
54 const vlc_fourcc_t * );
56 typedef struct
58 char *psz_file_template;
59 const char *psz_token;
60 } filter_sys_t;
62 /*****************************************************************************
63 * Module descriptor
64 *****************************************************************************/
66 #define SVG_TEMPLATE_BODY_TOKEN "<!--$SVGBODY$-->"
67 #define SVG_TEMPLATE_BODY_TOKEN_L 16
69 #define TEMPLATE_TEXT N_( "SVG template file" )
70 #define TEMPLATE_LONGTEXT N_( "Location of a file holding a SVG template "\
71 "for automatic string conversion" )
73 vlc_module_begin ()
74 set_category( CAT_INPUT )
75 set_category( SUBCAT_INPUT_SCODEC )
76 set_capability( "text renderer", 99 )
77 add_shortcut( "svg" )
78 add_string( "svg-template-file", "", TEMPLATE_TEXT, TEMPLATE_LONGTEXT, true )
79 set_callbacks( Create, Destroy )
80 vlc_module_end ()
82 static void svg_RescaletoFit ( filter_t *, int *width, int *height, float * );
83 static picture_t * svg_RenderPicture ( filter_t *p_filter, const char * );
85 static void svg_LoadTemplate( filter_t *p_filter )
87 filter_sys_t *p_sys = p_filter->p_sys;
88 char *psz_template = NULL;
89 char *psz_filename = var_InheritString( p_filter, "svg-template-file" );
90 if( psz_filename && psz_filename[0] )
92 /* Read the template */
93 FILE *file = vlc_fopen( psz_filename, "rt" );
94 if( !file )
96 msg_Warn( p_filter, "SVG template file %s does not exist.",
97 psz_filename );
99 else
101 struct stat s;
102 if( fstat( fileno( file ), &s ) || ((signed)s.st_size) < 0 )
104 msg_Err( p_filter, "SVG template invalid" );
106 else
108 msg_Dbg( p_filter, "reading %ld bytes from template %s",
109 (unsigned long)s.st_size, psz_filename );
111 psz_template = malloc( s.st_size + 1 );
112 if( psz_template )
114 psz_template[ s.st_size ] = 0;
115 ssize_t i_read = fread( psz_template, s.st_size, 1, file );
116 if( i_read != 1 )
118 free( psz_template );
119 psz_template = NULL;
123 fclose( file );
126 free( psz_filename );
128 if( psz_template )
130 p_sys->psz_token = strstr( psz_template, SVG_TEMPLATE_BODY_TOKEN );
131 if( !p_sys->psz_token )
133 msg_Err( p_filter, "'%s' not found in SVG template", SVG_TEMPLATE_BODY_TOKEN );
134 free( psz_template );
136 else *((char*)p_sys->psz_token) = 0;
139 p_sys->psz_file_template = psz_template;
142 static char *svg_GetDocument( filter_t *p_filter, int i_width, int i_height, const char *psz_body )
144 filter_sys_t *p_sys = p_filter->p_sys;
145 char *psz_result;
146 VLC_UNUSED(i_width);VLC_UNUSED(i_height);
148 if( p_sys->psz_file_template )
150 if( asprintf( &psz_result, "%s%s%s",
151 p_sys->psz_file_template,
152 psz_body,
153 &p_sys->psz_token[SVG_TEMPLATE_BODY_TOKEN_L] ) < 0 )
154 psz_result = NULL;
156 else
158 /* Either there was no file, or there was an error.
159 Use the default value */
160 const char *psz_temp = "<?xml version='1.0' encoding='UTF-8' standalone='no'?>"
161 "<svg preserveAspectRatio='xMinYMin meet'>" // viewBox='0 0 %d %d'>"
162 "<rect fill='none' width='100%%' height='100%%'></rect>"
163 "<text fill='white' font-family='sans-serif' font-size='32px'>%s</text>"
164 "</svg>";
165 if( asprintf( &psz_result, psz_temp, /*i_width, i_height,*/ psz_body ) < 0 )
166 psz_result = NULL;
169 return psz_result;
172 /*****************************************************************************
173 * Create: allocates svg video thread output method
174 *****************************************************************************
175 * This function allocates and initializes a vout method.
176 *****************************************************************************/
178 static int Create( vlc_object_t *p_this )
180 filter_t *p_filter = ( filter_t * )p_this;
182 filter_sys_t *p_sys = calloc( 1, sizeof(*p_sys) );
183 if( !p_sys )
184 return VLC_ENOMEM;
185 p_filter->p_sys = p_sys;
187 p_filter->pf_render = RenderText;
188 svg_LoadTemplate( p_filter );
190 #if (GLIB_MAJOR_VERSION < 2 || GLIB_MINOR_VERSION < 36)
191 g_type_init( );
192 #endif
194 return VLC_SUCCESS;
197 /*****************************************************************************
198 * Destroy: destroy Clone video thread output method
199 *****************************************************************************
200 * Clean up all data and library connections
201 *****************************************************************************/
202 static void Destroy( vlc_object_t *p_this )
204 filter_t *p_filter = ( filter_t * )p_this;
205 filter_sys_t *p_sys = p_filter->p_sys;
206 #if (GLIB_MAJOR_VERSION < 2 || GLIB_MINOR_VERSION < 36)
207 rsvg_term();
208 #endif
209 free( p_sys->psz_file_template );
210 free( p_sys );
213 static void svg_RescaletoFit( filter_t *p_filter, int *width, int *height, float *scale )
215 *scale = 1.0;
217 if( *width > 0 && *height > 0 )
219 if( (unsigned)*width > p_filter->fmt_out.video.i_visible_width )
220 *scale = (1.0 * p_filter->fmt_out.video.i_visible_width / *width);
222 if( (unsigned)*height > p_filter->fmt_out.video.i_visible_height )
224 float y_scale = (1.0 * p_filter->fmt_out.video.i_visible_height / *height);
225 if( y_scale < *scale )
226 *scale = y_scale;
229 *width *= *scale;
230 *height *= *scale;
234 static picture_t * svg_RenderPicture( filter_t *p_filter,
235 const char *psz_svgdata )
237 RsvgHandle *p_handle;
238 GError *error = NULL;
240 p_handle = rsvg_handle_new_from_data( (const guint8 *)psz_svgdata,
241 strlen( psz_svgdata ), &error );
242 if( !p_handle )
244 msg_Err( p_filter, "error while rendering SVG: %s", error->message );
245 return NULL;
248 RsvgDimensionData dim;
249 rsvg_handle_get_dimensions( p_handle, &dim );
250 float scale;
251 svg_RescaletoFit( p_filter, &dim.width, &dim.height, &scale );
253 /* Create a new subpicture region */
254 video_format_t fmt;
255 video_format_Init( &fmt, VLC_CODEC_BGRA ); /* CAIRO_FORMAT_ARGB32 == VLC_CODEC_BGRA, go figure */
256 fmt.i_bits_per_pixel = 32;
257 fmt.i_chroma = VLC_CODEC_BGRA;
258 fmt.i_width = fmt.i_visible_width = dim.width;
259 fmt.i_height = fmt.i_visible_height = dim.height;
261 picture_t *p_picture = picture_NewFromFormat( &fmt );
262 if( !p_picture )
264 video_format_Clean( &fmt );
265 g_object_unref( G_OBJECT( p_handle ) );
266 return NULL;
268 memset( p_picture->p[0].p_pixels, 0x00, p_picture->p[0].i_pitch * p_picture->p[0].i_lines );
270 cairo_surface_t* surface = cairo_image_surface_create_for_data( p_picture->p->p_pixels,
271 CAIRO_FORMAT_ARGB32,
272 fmt.i_width, fmt.i_height,
273 p_picture->p[0].i_pitch );
274 if( !surface )
276 g_object_unref( G_OBJECT( p_handle ) );
277 picture_Release( p_picture );
278 return NULL;
281 cairo_t *cr = cairo_create( surface );
282 if( !cr )
284 msg_Err( p_filter, "error while creating cairo surface" );
285 cairo_surface_destroy( surface );
286 g_object_unref( G_OBJECT( p_handle ) );
287 picture_Release( p_picture );
288 return NULL;
291 if( ! rsvg_handle_render_cairo( p_handle, cr ) )
293 msg_Err( p_filter, "error while rendering SVG" );
294 cairo_destroy( cr );
295 cairo_surface_destroy( surface );
296 g_object_unref( G_OBJECT( p_handle ) );
297 picture_Release( p_picture );
298 return NULL;
301 cairo_destroy( cr );
302 cairo_surface_destroy( surface );
303 g_object_unref( G_OBJECT( p_handle ) );
305 return p_picture;
308 static char * SegmentsToSVG( text_segment_t *p_segment, int i_height, int *pi_total_size )
310 char *psz_result = NULL;
312 i_height = 6 * i_height / 100;
313 *pi_total_size = 0;
315 for( ; p_segment; p_segment = p_segment->p_next )
317 char *psz_prev = psz_result;
318 char *psz_encoded = vlc_xml_encode( p_segment->psz_text );
319 if( asprintf( &psz_result, "%s<tspan x='0' dy='%upx'>%s</tspan>\n",
320 (psz_prev) ? psz_prev : "",
321 i_height,
322 psz_encoded ) < 0 )
323 psz_result = NULL;
324 free( psz_prev );
325 free( psz_encoded );
327 *pi_total_size += i_height;
330 return psz_result;
333 static int RenderText( filter_t *p_filter, subpicture_region_t *p_region_out,
334 subpicture_region_t *p_region_in,
335 const vlc_fourcc_t *p_chroma_list )
337 /* Sanity check */
338 if( !p_region_in || !p_region_out || !p_region_in->p_text )
339 return VLC_EGENERIC;
341 for( size_t i=0; p_chroma_list[i]; i++ )
343 if( p_chroma_list[i] == VLC_CODEC_BGRA )
344 break;
345 if( p_chroma_list[i] == 0 )
346 return VLC_EGENERIC;
349 p_region_out->i_x = p_region_in->i_x;
350 p_region_out->i_y = p_region_in->i_y;
352 unsigned i_width = p_filter->fmt_out.video.i_visible_width;
353 if( (unsigned) p_region_out->i_x <= i_width )
354 i_width -= p_region_out->i_x;
356 unsigned i_height = p_filter->fmt_out.video.i_visible_height;
357 if( (unsigned) p_region_out->i_y <= i_height )
358 i_height -= p_region_out->i_y;
360 if( i_height == 0 || i_width == 0 )
361 return VLC_EGENERIC;
363 char *psz_svg;
364 /* Check if the data is SVG or pure text. In the latter case,
365 convert the text to SVG. FIXME: find a better test */
366 if( p_region_in->p_text && strstr( p_region_in->p_text->psz_text, "<svg" ) )
368 psz_svg = strdup( p_region_in->p_text->psz_text );
370 else
372 /* Data is text. Convert to SVG */
373 int i_total;
374 psz_svg = SegmentsToSVG( p_region_in->p_text, i_height, &i_total );
375 if( psz_svg )
377 char *psz_doc = svg_GetDocument( p_filter, i_width, i_total, psz_svg );
378 free( psz_svg );
379 psz_svg = psz_doc;
383 if( !psz_svg )
384 return VLC_EGENERIC;
386 picture_t *p_picture = svg_RenderPicture( p_filter, psz_svg );
388 free( psz_svg );
390 if (p_picture)
392 p_region_out->p_picture = p_picture;
393 video_format_Clean( &p_region_out->fmt );
394 video_format_Copy( &p_region_out->fmt, &p_picture->format );
395 return VLC_SUCCESS;
397 return VLC_EGENERIC;