[vlc-devel] [PATCH] SVG rendering
Olivier Aubert
oaubert at bat710.univ-lyon1.fr
Mon Jul 21 18:12:21 CEST 2003
Hello
Here is a first shot at a SVG rendering plugin. It uses the new text
rendering architecture, but takes SVG instead of plain text as input. It
uses the librsvg2 library from GNOME (hence it has the same
limitations), but works fairly well.
It is not optimized for speed (just look at the Render code...), but I
am not expert enough in image manipulation matters to make it better.
Some VLC wizard could write chroma conversion routines usable from
anywhere...
The use of the text rendering architecture makes some parameters in the
new interface useless (text style, flags and margins). They are ignored
for the moment, but I could find some use for them (for instance, the
SVG is rendered at the same size as the video output. We could do it at
the SVG native size).
Anyway, there is still one point : how to do a precise selection of the
type of renderer that we want ? For my tests, I simply deleted the
freetype module, but I think it is an issue.
Olivier
PS: Attached is the patch against the current CVS tree (as of the date
of this email).
-------------- next part --------------
--- vlc-cvs/configure.ac 2003-07-21 10:53:48.000000000 +0200
+++ vlc-test-cvs/configure.ac 2003-07-18 17:37:58.000000000 +0200
@@ -2152,6 +2142,29 @@
fi
dnl
+dnl SVG module
+dnl
+AC_ARG_ENABLE(svg,
+ [ --enable-svg svg support (default disabled)])
+if test "${enable_svg}" == "yes"
+then
+ LIBRSVG=librsvg-2.0
+ PKG_CONFIG=pkg-config
+ if ${PKG_CONFIG} --exists ${LIBRSVG}
+ then
+ AX_ADD_PLUGINS([svg])
+ AX_ADD_CFLAGS([svg],[`${PKG_CONFIG} --cflags ${LIBRSVG}`])
+ AX_ADD_LDFLAGS([svg],[`${PKG_CONFIG} --libs ${LIBRSVG}`])
+ CPPFLAGS="${CPPFLAGS_save} ${CFLAGS_svg}"
+ elif test "${enable_svg}" = "yes"
+ then
+ AC_MSG_ERROR([I couldn't find the svg package. You can download librsvg2
+from http://www.gnoome.org/.
+ ])
+ fi
+fi
+
+dnl
dnl Qt Embedded module
dnl (disabled by default)
dnl
--- vlc-cvs/modules/misc/Modules.am 2003-07-18 17:32:35.000000000 +0200
+++ vlc-test-cvs/modules/misc/Modules.am 2003-07-18 18:17:12.000000000 +0200
@@ -7,3 +7,4 @@
SOURCES_qte_main = qte_main.cpp
SOURCES_freetype = freetype.c
SOURCES_httpd = httpd.c
+SOURCES_svg = svg.c
--- vlc-cvs/modules/misc/svg.c 1970-01-01 01:00:00.000000000 +0100
+++ vlc-test-cvs/modules/misc/svg.c 2003-07-21 17:50:29.000000000 +0200
@@ -0,0 +1,344 @@
+/*****************************************************************************
+ * freetype.c : Put SVG on the video
+ *****************************************************************************
+ * Copyright (C) 2002, 2003 VideoLAN
+ * $Id: $
+ *
+ * Authors: Olivier Aubert <oaubert at lisi.univ-lyon1.fr>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
+ *****************************************************************************/
+
+/*****************************************************************************
+ * Preamble
+ *****************************************************************************/
+#include <stdlib.h> /* malloc(), free() */
+#include <string.h>
+
+#include <vlc/vlc.h>
+#include <vlc/vout.h>
+
+#include <librsvg-2/librsvg/rsvg.h>
+
+/*****************************************************************************
+ * Local prototypes
+ *****************************************************************************/
+static int Create ( vlc_object_t * );
+static void Destroy ( vlc_object_t * );
+
+static void Render ( vout_thread_t *, picture_t *,
+ const subpicture_t * );
+static int AddText ( vout_thread_t *p_vout, byte_t *psz_string,
+ text_style_t *p_style, int i_flags, int i_hmargin,
+ int i_vmargin, mtime_t i_start, mtime_t i_stop );
+static void svg_size_callback (int *width, int *height, gpointer data);
+static void svg_render_picture (vout_thread_t *p_vout,
+ subpicture_sys_t *p_string);
+static void FreeString( subpicture_t * );
+
+/*****************************************************************************
+ * Module descriptor
+ *****************************************************************************/
+vlc_module_begin();
+ set_capability( "text renderer", 100 );
+ add_shortcut( "svg" );
+ set_callbacks( Create, Destroy );
+vlc_module_end();
+
+/**
+ Describes a SVG string to be displayed on the video
+*/
+struct subpicture_sys_t
+{
+ int i_width;
+ int i_height;
+ int i_chroma;
+ /** The SVG source associated with this subpicture */
+ byte_t *psz_text;
+ /* The rendered SVG, as a GdkPixbuf */
+ GdkPixbuf *p_rendition;
+};
+
+/*****************************************************************************
+ * vout_sys_t: svg local data
+ *****************************************************************************
+ * This structure is part of the video output thread descriptor.
+ * It describes the svg specific properties of an output thread.
+ *****************************************************************************/
+struct text_renderer_sys_t
+{
+ vlc_mutex_t *lock;
+};
+
+/*****************************************************************************
+ * Create: allocates svg video thread output method
+ *****************************************************************************
+ * This function allocates and initializes a vout method.
+ *****************************************************************************/
+static int Create( vlc_object_t *p_this )
+{
+ vout_thread_t *p_vout = (vout_thread_t *)p_this;
+
+ /* Allocate structure */
+ p_vout->p_text_renderer_data = malloc( sizeof( text_renderer_sys_t ) );
+ if( p_vout->p_text_renderer_data == NULL )
+ {
+ msg_Err( p_vout, "out of memory" );
+ return VLC_ENOMEM;
+ }
+
+ p_vout->pf_add_string = AddText;
+ return VLC_SUCCESS;
+}
+
+/*****************************************************************************
+ * Destroy: destroy Clone video thread output method
+ *****************************************************************************
+ * Clean up all data and library connections
+ *****************************************************************************/
+static void Destroy( vlc_object_t *p_this )
+{
+ vout_thread_t *p_vout = (vout_thread_t *)p_this;
+ free( p_vout->p_text_renderer_data );
+}
+
+/*****************************************************************************
+ * Render: render SVG in picture
+ *****************************************************************************
+ * This function merges the previously rendered SVG subpicture into a picture
+ *****************************************************************************/
+static void Render( vout_thread_t *p_vout, picture_t *p_pic,
+ const subpicture_t *p_subpic )
+{
+ subpicture_sys_t *p_string = p_subpic->p_sys;
+ guchar *pixels_in = NULL;
+ guchar *pixels_out = NULL;
+ int rowstride_in, rowstride_out;
+ int channels_in, channels_out;
+ int x, y;
+ int i_width, i_height;
+ int alpha;
+
+ if (p_string->p_rendition == NULL) {
+ /* Something changed (presumably the dimensions). Get the new
+ dimensions and update the pixbuf */
+ p_string->i_width = p_vout->output.i_width;
+ p_string->i_height = p_vout->output.i_height;
+ svg_render_picture (p_vout, p_string);
+ }
+
+ /*
+ p_pixbuf->get_rowstride () is the number of bytes in a line.
+ p_pixbuf->get_height () is the number of lines.
+
+ The number of bytes of p_pixbuf->p_pixels is get_rowstride * get_height
+
+ if (has_alpha ()) {
+ alpha = pixels [ n_channels * (y*rowstride + x) + 3 ];
+ }
+ red = pixels [ n_channels * (y*rowstride) + x) ];
+ green = pixels [ n_channels * (y*rowstride) + x) + 1 ];
+ blue = pixels [ n_channels * (y*rowstride) + x) + 2 ];
+
+ */
+
+ pixels_in = gdk_pixbuf_get_pixels (p_string->p_rendition);
+ pixels_out = p_pic->p->p_pixels;
+
+ rowstride_in = gdk_pixbuf_get_rowstride (p_string->p_rendition);
+ rowstride_out = p_pic->p->i_pitch;
+
+ channels_in = gdk_pixbuf_get_n_channels (p_string->p_rendition);
+ channels_out = p_pic->p->i_pixel_pitch;
+
+ alpha = gdk_pixbuf_get_has_alpha (p_string->p_rendition);
+
+#define INDEX_IN(x, y) (y * rowstride_in + x * channels_in)
+#define INDEX_OUT(x, y) (y * rowstride_out + x * channels_out)
+#define UV_INDEX_OUT(x, y) (y * p_pic->p[U_PLANE].i_pitch / 2 + x * p_pic->p[U_PLANE].i_pixel_pitch / 2)
+
+ i_width = gdk_pixbuf_get_width (p_string->p_rendition);
+ i_height = gdk_pixbuf_get_height (p_string->p_rendition);
+
+ /* Brute force rendering. Someone better in image manipulation
+ should do that. */
+ switch( p_vout->output.i_chroma )
+ {
+ /* I420 target, no scaling */
+ case VLC_FOURCC('I','4','2','0'):
+ case VLC_FOURCC('I','Y','U','V'):
+ case VLC_FOURCC('Y','V','1','2'):
+ for(y = 0; y < i_height; y++ )
+ {
+ for( x = 0; x < i_width; x++ )
+ {
+ guchar *p_in;
+ int i_out;
+ int i_uv_out;
+
+ p_in = &pixels_in[INDEX_IN(x, y)];
+
+#define R(pixel) *pixel
+#define G(pixel) *(pixel+1)
+#define B(pixel) *(pixel+2)
+#define ALPHA(pixel) *(pixel+3)
+#define BLEND(in, out, alpha) (out) = (((alpha) * (in) + (255 - (alpha)) * (out)) / 256)
+
+ /* From http://www.geocrawler.com/archives/3/8263/2001/6/0/6020594/ :
+ Y = 0.29900 * R + 0.58700 * G + 0.11400 * B
+ U = -0.1687 * r - 0.3313 * g + 0.5 * b + 128
+ V = 0.5 * r - 0.4187 * g - 0.0813 * b + 128
+ */
+ /* FIXME: We could save a test in the loop by putting the alpha
+ test outside of the for loops, but this is for debugging */
+ if ((alpha && ALPHA(p_in) > 10) || (! alpha)) {
+ i_out = INDEX_OUT(x, y);
+
+ BLEND((.299 * R(p_in) + .587 * G(p_in) + .114 * B(p_in)), p_pic->p[Y_PLANE].p_pixels[i_out], ALPHA(p_in));
+
+ if( ( x % 2 == 0 ) && ( y % 2 == 0 ) ) {
+ i_uv_out = UV_INDEX_OUT(x, y);
+
+ BLEND(-.1687 * R(p_in) - .3313 * G(p_in) + .5 * B(p_in) + 128, p_pic->p[U_PLANE].p_pixels[i_uv_out], ALPHA(p_in));
+
+ BLEND(.5 * R(p_in) - .4187 * G(p_in) - .0813 * B(p_in) + 128, p_pic->p[V_PLANE].p_pixels[i_uv_out], ALPHA(p_in));
+ }
+ }
+ }
+ }
+ break;
+
+ /* RV32 target, scaling */
+ case VLC_FOURCC('R','V','2','4'):
+ case VLC_FOURCC('R','V','3','2'):
+ for(y = 0; y < i_height; y++ )
+ {
+ for( x = 0; x < i_width; x++ )
+ {
+ guchar *p_in;
+ guchar *p_out;
+
+ p_in = &pixels_in[INDEX_IN(x, y)];
+ p_out = &pixels_out[INDEX_OUT(x, y)];
+
+ *p_out = *p_in;
+ *(p_out+1) = *(p_in+1);
+ *(p_out+2) = *(p_in+2);
+ }
+ }
+ break;
+
+ default:
+ msg_Err( p_vout, "unknown chroma, can't render SPU" );
+ break;
+ }
+}
+
+static void svg_size_callback (int *width, int *height, gpointer data)
+{
+ subpicture_sys_t *p_string = data;
+
+ *width = p_string->i_width;
+ *height = p_string->i_height;
+ return;
+}
+
+static void svg_render_picture (vout_thread_t *p_vout,
+ subpicture_sys_t *p_string)
+{
+ /* Render the SVG string p_string->psz_text into a new picture_t
+ p_string->p_rendition with dimensions (->i_width, ->i_height) */
+ RsvgHandle *p_handle;
+ GError *error;
+
+ p_handle = rsvg_handle_new ();
+
+ rsvg_handle_set_size_callback (p_handle, svg_size_callback, p_string, NULL);
+
+ rsvg_handle_write (p_handle,
+ p_string->psz_text, strlen (p_string->psz_text) + 1,
+ &error);
+ rsvg_handle_close (p_handle, &error);
+
+ p_string->p_rendition = rsvg_handle_get_pixbuf (p_handle);
+ rsvg_handle_free (p_handle);
+}
+
+
+/**
+ * This function receives a SVG string and creates a subpicture for it.
+ * It is used as pf_add_string callback in the vout method by this module.
+ */
+static int AddText ( vout_thread_t *p_vout, byte_t *psz_string,
+ text_style_t *p_style, int i_flags, int i_hmargin,
+ int i_vmargin, mtime_t i_start, mtime_t i_stop )
+{
+ subpicture_sys_t *p_string;
+ subpicture_t *p_subpic;
+
+ msg_Dbg( p_vout, "adding string \"%s\" start_date "I64Fd
+ " end_date" I64Fd, psz_string, i_start, i_stop );
+
+ /* Create and initialize a subpicture */
+ p_subpic = vout_CreateSubPicture( p_vout, MEMORY_SUBPICTURE );
+ if ( p_subpic == NULL )
+ {
+ return VLC_EGENERIC;
+ }
+
+ p_subpic->pf_render = Render;
+ p_subpic->pf_destroy = FreeString;
+ p_subpic->i_start = i_start;
+ p_subpic->i_stop = i_stop;
+ if( i_stop == 0 )
+ {
+ p_subpic->b_ephemer = VLC_TRUE;
+ }
+ else
+ {
+ p_subpic->b_ephemer = VLC_FALSE;
+ }
+
+ /* Create and initialize private data for the subpicture */
+ p_string = malloc( sizeof(subpicture_sys_t) );
+ if ( p_string == NULL )
+ {
+ vout_DestroySubPicture( p_vout, p_subpic );
+ return VLC_ENOMEM;
+ }
+ p_subpic->p_sys = p_string;
+
+ p_string->psz_text = strdup( psz_string );
+ p_string->i_width = p_vout->output.i_width;
+ p_string->i_height = p_vout->output.i_height;
+ p_string->i_chroma = p_vout->output.i_chroma;
+
+ /* Render the SVG.
+ The input data is stored in the p_string structure,
+ and the function updates the p_rendition attribute. */
+ svg_render_picture (p_vout, p_string);
+
+ vout_DisplaySubPicture( p_vout, p_subpic );
+ return VLC_SUCCESS;
+}
+
+static void FreeString( subpicture_t *p_subpic )
+{
+ subpicture_sys_t *p_string = p_subpic->p_sys;
+
+ free( p_string->psz_text );
+ free( p_string->p_rendition );
+ free( p_string );
+}
More information about the vlc-devel
mailing list