2 * Copyright (C) <2009> Sebastian Dröge <sebastian.droege@collabora.co.uk>
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17 * Boston, MA 02111-1307, USA.
20 * SECTION:element-rsvgdec
22 * This elements renders SVG graphics.
25 * <title>Example launch lines</title>
27 * gst-launch filesrc location=image.svg ! rsvgdec ! imagefreeze ! ffmpegcolorspace ! autovideosink
28 * ]| render and show a svg image.
36 #include "gstrsvgdec.h"
40 GST_DEBUG_CATEGORY_STATIC (rsvgdec_debug);
41 #define GST_CAT_DEFAULT rsvgdec_debug
43 static GstStaticPadTemplate sink_factory =
44 GST_STATIC_PAD_TEMPLATE ("sink", GST_PAD_SINK, GST_PAD_ALWAYS,
45 GST_STATIC_CAPS ("image/svg+xml; image/svg"));
47 #if G_BYTE_ORDER == G_LITTLE_ENDIAN
48 #define GST_RSVG_VIDEO_CAPS GST_VIDEO_CAPS_BGRA
50 #define GST_RSVG_VIDEO_CAPS GST_VIDEO_CAPS_ARGB
53 static GstStaticPadTemplate src_factory =
54 GST_STATIC_PAD_TEMPLATE ("src", GST_PAD_SRC, GST_PAD_ALWAYS,
55 GST_STATIC_CAPS (GST_RSVG_VIDEO_CAPS));
57 GST_BOILERPLATE (GstRsvgDec, gst_rsvg_dec, GstElement, GST_TYPE_ELEMENT);
59 static void gst_rsvg_dec_reset (GstRsvgDec * rsvg);
61 static GstFlowReturn gst_rsvg_dec_chain (GstPad * pad, GstBuffer * buffer);
62 static gboolean gst_rsvg_dec_sink_set_caps (GstPad * pad, GstCaps * caps);
63 static gboolean gst_rsvg_dec_sink_event (GstPad * pad, GstEvent * event);
65 static gboolean gst_rsvg_dec_src_event (GstPad * pad, GstEvent * event);
66 static gboolean gst_rsvg_dec_src_query (GstPad * pad, GstQuery * query);
67 static const GstQueryType *gst_rsvg_dec_src_query_type (GstPad * pad);
68 static gboolean gst_rsvg_dec_src_set_caps (GstPad * pad, GstCaps * caps);
70 static GstStateChangeReturn gst_rsvg_dec_change_state (GstElement * element,
71 GstStateChange transition);
73 static void gst_rsvg_dec_finalize (GObject * object);
76 gst_rsvg_dec_base_init (gpointer g_class)
78 GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
80 gst_element_class_set_details_simple (element_class,
81 "SVG image decoder", "Codec/Decoder/Image",
82 "Uses librsvg to decode SVG images",
83 "Sebastian Dröge <sebastian.droege@collabora.co.uk>");
85 gst_element_class_add_static_pad_template (element_class, &sink_factory);
86 gst_element_class_add_static_pad_template (element_class, &src_factory);
90 gst_rsvg_dec_class_init (GstRsvgDecClass * klass)
92 GstElementClass *element_class = (GstElementClass *) klass;
93 GObjectClass *gobject_class = (GObjectClass *) klass;
95 GST_DEBUG_CATEGORY_INIT (rsvgdec_debug, "rsvgdec", 0, "RSVG decoder");
97 gobject_class->finalize = gst_rsvg_dec_finalize;
98 element_class->change_state = GST_DEBUG_FUNCPTR (gst_rsvg_dec_change_state);
102 gst_rsvg_dec_init (GstRsvgDec * rsvg, GstRsvgDecClass * klass)
104 rsvg->sinkpad = gst_pad_new_from_static_template (&sink_factory, "sink");
105 gst_pad_set_setcaps_function (rsvg->sinkpad, gst_rsvg_dec_sink_set_caps);
106 gst_pad_set_event_function (rsvg->sinkpad, gst_rsvg_dec_sink_event);
107 gst_pad_set_chain_function (rsvg->sinkpad, gst_rsvg_dec_chain);
108 gst_element_add_pad (GST_ELEMENT (rsvg), rsvg->sinkpad);
110 rsvg->srcpad = gst_pad_new_from_static_template (&src_factory, "src");
111 gst_pad_set_event_function (rsvg->srcpad, gst_rsvg_dec_src_event);
112 gst_pad_set_query_function (rsvg->srcpad, gst_rsvg_dec_src_query);
113 gst_pad_set_query_type_function (rsvg->srcpad, gst_rsvg_dec_src_query_type);
114 gst_pad_set_setcaps_function (rsvg->srcpad, gst_rsvg_dec_src_set_caps);
115 gst_element_add_pad (GST_ELEMENT (rsvg), rsvg->srcpad);
117 rsvg->adapter = gst_adapter_new ();
119 gst_rsvg_dec_reset (rsvg);
123 gst_rsvg_dec_finalize (GObject * object)
125 GstRsvgDec *rsvg = GST_RSVG_DEC (object);
128 g_object_unref (rsvg->adapter);
129 rsvg->adapter = NULL;
132 G_OBJECT_CLASS (parent_class)->finalize (object);
136 gst_rsvg_dec_reset (GstRsvgDec * dec)
138 gst_adapter_clear (dec->adapter);
139 dec->width = dec->height = 0;
142 dec->first_timestamp = GST_CLOCK_TIME_NONE;
143 dec->frame_count = 0;
145 gst_segment_init (&dec->segment, GST_FORMAT_UNDEFINED);
146 dec->need_newsegment = TRUE;
148 g_list_foreach (dec->pending_events, (GFunc) gst_mini_object_unref, NULL);
149 g_list_free (dec->pending_events);
150 dec->pending_events = NULL;
152 if (dec->pending_tags) {
153 gst_tag_list_free (dec->pending_tags);
154 dec->pending_tags = NULL;
158 #define CAIRO_UNPREMULTIPLY(a,r,g,b) G_STMT_START { \
159 b = (a > 0) ? MIN ((b * 255 + a / 2) / a, 255) : 0; \
160 g = (a > 0) ? MIN ((g * 255 + a / 2) / a, 255) : 0; \
161 r = (a > 0) ? MIN ((r * 255 + a / 2) / a, 255) : 0; \
165 gst_rsvg_decode_unpremultiply (guint8 * data, gint width, gint height)
170 for (i = 0; i < height; i++) {
171 for (j = 0; j < width; j++) {
172 #if G_BYTE_ORDER == G_LITTLE_ENDIAN
174 data[0] = (a > 0) ? MIN ((data[0] * 255 + a / 2) / a, 255) : 0;
175 data[1] = (a > 0) ? MIN ((data[1] * 255 + a / 2) / a, 255) : 0;
176 data[2] = (a > 0) ? MIN ((data[2] * 255 + a / 2) / a, 255) : 0;
179 data[1] = (a > 0) ? MIN ((data[1] * 255 + a / 2) / a, 255) : 0;
180 data[2] = (a > 0) ? MIN ((data[2] * 255 + a / 2) / a, 255) : 0;
181 data[3] = (a > 0) ? MIN ((data[3] * 255 + a / 2) / a, 255) : 0;
189 gst_rsvg_decode_image (GstRsvgDec * rsvg, const guint8 * data, guint size,
192 GstFlowReturn ret = GST_FLOW_OK;
194 cairo_surface_t *surface;
196 GError *error = NULL;
197 RsvgDimensionData dimension;
198 gdouble scalex, scaley;
199 const gchar *title = NULL, *comment = NULL;
201 GST_LOG_OBJECT (rsvg, "parsing svg");
203 handle = rsvg_handle_new_from_data (data, size, &error);
205 GST_ERROR_OBJECT (rsvg, "Failed to parse SVG image: %s", error->message);
206 g_error_free (error);
207 return GST_FLOW_ERROR;
210 title = rsvg_handle_get_title (handle);
211 comment = rsvg_handle_get_desc (handle);
213 if (title || comment) {
214 GST_LOG_OBJECT (rsvg, "adding tags");
216 if (!rsvg->pending_tags)
217 rsvg->pending_tags = gst_tag_list_new ();
220 gst_tag_list_add (rsvg->pending_tags, GST_TAG_MERGE_REPLACE_ALL,
221 GST_TAG_TITLE, title, NULL);
222 if (comment && *comment)
223 gst_tag_list_add (rsvg->pending_tags, GST_TAG_MERGE_REPLACE_ALL,
224 GST_TAG_COMMENT, comment, NULL);
227 rsvg_handle_get_dimensions (handle, &dimension);
228 if (rsvg->width != dimension.width || rsvg->height != dimension.height) {
229 GstCaps *caps1, *caps2, *caps3;
232 GST_LOG_OBJECT (rsvg, "resolution changed, updating caps");
234 caps1 = gst_caps_copy (gst_pad_get_pad_template_caps (rsvg->srcpad));
235 caps2 = gst_pad_peer_get_caps (rsvg->srcpad);
237 caps3 = gst_caps_intersect (caps1, caps2);
238 gst_caps_unref (caps1);
239 gst_caps_unref (caps2);
244 if (gst_caps_is_empty (caps1)) {
245 GST_ERROR_OBJECT (rsvg, "Unable to negotiate a format");
246 gst_caps_unref (caps1);
247 g_object_unref (handle);
248 return GST_FLOW_NOT_NEGOTIATED;
251 caps2 = gst_caps_copy (gst_pad_get_pad_template_caps (rsvg->srcpad));
252 s = gst_caps_get_structure (caps2, 0);
253 gst_structure_set (s, "width", G_TYPE_INT, dimension.width, "height",
254 G_TYPE_INT, dimension.height, "framerate", GST_TYPE_FRACTION, 0, 1,
256 caps3 = gst_caps_intersect (caps1, caps2);
257 if (!gst_caps_is_empty (caps3)) {
258 gst_caps_truncate (caps3);
259 gst_pad_set_caps (rsvg->srcpad, caps3);
260 gst_caps_unref (caps1);
261 gst_caps_unref (caps2);
262 gst_caps_unref (caps3);
263 rsvg->width = dimension.width;
264 rsvg->height = dimension.height;
266 gst_caps_unref (caps2);
267 gst_caps_unref (caps3);
268 gst_caps_truncate (caps1);
270 s = gst_caps_get_structure (caps1, 0);
271 gst_structure_set (s, "framerate", GST_TYPE_FRACTION, 0, 1, NULL);
273 if (!gst_caps_is_fixed (caps1)
274 && (!gst_structure_fixate_field_nearest_int (s, "width",
276 || !gst_structure_fixate_field_nearest_int (s, "height",
277 dimension.height))) {
278 g_object_unref (handle);
279 GST_ERROR_OBJECT (rsvg, "Failed to fixate caps");
280 return GST_FLOW_NOT_NEGOTIATED;
282 gst_pad_set_caps (rsvg->srcpad, caps1);
283 gst_structure_get_int (s, "width", &rsvg->width);
284 gst_structure_get_int (s, "height", &rsvg->height);
285 gst_caps_unref (caps1);
289 if ((ret = gst_pad_alloc_buffer_and_set_caps (rsvg->srcpad,
290 GST_BUFFER_OFFSET_NONE,
291 rsvg->width * rsvg->height * 4,
292 GST_PAD_CAPS (rsvg->srcpad), buffer)) != GST_FLOW_OK) {
293 g_object_unref (handle);
294 GST_ERROR_OBJECT (rsvg, "Buffer allocation failed %s",
295 gst_flow_get_name (ret));
299 GST_LOG_OBJECT (rsvg, "render image at %d x %d", rsvg->height, rsvg->width);
302 cairo_image_surface_create_for_data (GST_BUFFER_DATA (*buffer),
303 CAIRO_FORMAT_ARGB32, rsvg->width, rsvg->height, rsvg->width * 4);
305 cr = cairo_create (surface);
306 cairo_set_operator (cr, CAIRO_OPERATOR_CLEAR);
307 cairo_set_source_rgba (cr, 1.0, 1.0, 1.0, 0.0);
309 cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
310 cairo_set_source_rgba (cr, 0.0, 0.0, 0.0, 1.0);
312 scalex = scaley = 1.0;
313 if (rsvg->width != dimension.width) {
314 scalex = ((gdouble) rsvg->width) / ((gdouble) dimension.width);
316 if (rsvg->height != dimension.height) {
317 scaley = ((gdouble) rsvg->height) / ((gdouble) dimension.height);
319 cairo_scale (cr, scalex, scaley);
320 rsvg_handle_render_cairo (handle, cr);
322 g_object_unref (handle);
324 cairo_surface_destroy (surface);
326 /* Now unpremultiply Cairo's ARGB to match GStreamer's */
327 gst_rsvg_decode_unpremultiply (GST_BUFFER_DATA (*buffer), rsvg->width,
334 gst_rsvg_dec_chain (GstPad * pad, GstBuffer * buffer)
336 GstRsvgDec *rsvg = GST_RSVG_DEC (GST_PAD_PARENT (pad));
337 gboolean completed = FALSE;
340 gboolean ret = GST_FLOW_OK;
342 /* first_timestamp is used slightly differently where a framerate
344 If there is a frame rate, it will be used as a base.
345 If there is not, it will be used to keep track of the timestamp
346 of the first buffer, to be used as the timestamp of the output
347 buffer. When a buffer is output, first timestamp will resync to
348 the next buffer's timestamp. */
349 if (rsvg->first_timestamp == GST_CLOCK_TIME_NONE) {
350 if (GST_BUFFER_TIMESTAMP_IS_VALID (buffer))
351 rsvg->first_timestamp = GST_BUFFER_TIMESTAMP (buffer);
352 else if (rsvg->fps_n != 0)
353 rsvg->first_timestamp = 0;
356 gst_adapter_push (rsvg->adapter, buffer);
358 size = gst_adapter_available (rsvg->adapter);
361 while (size >= 5 + 6 && ret == GST_FLOW_OK) {
364 data = gst_adapter_peek (rsvg->adapter, size);
365 for (i = size - 6; i >= 5; i--) {
366 if (memcmp (data + i, "</svg>", 6) == 0) {
374 GstBuffer *outbuf = NULL;
376 GST_LOG_OBJECT (rsvg, "have complete svg of %u bytes", size);
378 data = gst_adapter_peek (rsvg->adapter, size);
380 ret = gst_rsvg_decode_image (rsvg, data, size, &outbuf);
381 if (ret != GST_FLOW_OK)
385 if (rsvg->first_timestamp != GST_CLOCK_TIME_NONE) {
386 GST_BUFFER_TIMESTAMP (outbuf) = rsvg->first_timestamp;
387 GST_BUFFER_DURATION (outbuf) = GST_CLOCK_TIME_NONE;
388 if (GST_BUFFER_DURATION_IS_VALID (buffer)) {
390 GST_BUFFER_TIMESTAMP_IS_VALID (buffer) ?
391 GST_BUFFER_TIMESTAMP (buffer) : rsvg->first_timestamp;
392 end += GST_BUFFER_DURATION (buffer);
393 GST_BUFFER_DURATION (outbuf) = end - GST_BUFFER_TIMESTAMP (outbuf);
395 if (rsvg->fps_n == 0) {
396 rsvg->first_timestamp = GST_CLOCK_TIME_NONE;
398 GST_BUFFER_DURATION (outbuf) =
399 gst_util_uint64_scale (rsvg->frame_count, rsvg->fps_d,
400 rsvg->fps_n * GST_SECOND);
402 } else if (rsvg->fps_n != 0) {
403 GST_BUFFER_TIMESTAMP (outbuf) =
404 rsvg->first_timestamp + gst_util_uint64_scale (rsvg->frame_count,
405 rsvg->fps_d, rsvg->fps_n * GST_SECOND);
406 GST_BUFFER_DURATION (outbuf) =
407 gst_util_uint64_scale (rsvg->frame_count, rsvg->fps_d,
408 rsvg->fps_n * GST_SECOND);
410 GST_BUFFER_TIMESTAMP (outbuf) = rsvg->first_timestamp;
411 GST_BUFFER_DURATION (outbuf) = GST_CLOCK_TIME_NONE;
415 if (rsvg->need_newsegment) {
416 gst_pad_push_event (rsvg->srcpad,
417 gst_event_new_new_segment (FALSE, 1.0, GST_FORMAT_TIME, 0, -1, 0));
418 rsvg->need_newsegment = FALSE;
421 if (rsvg->pending_events) {
424 for (l = rsvg->pending_events; l; l = l->next)
425 gst_pad_push_event (rsvg->srcpad, l->data);
426 g_list_free (rsvg->pending_events);
427 rsvg->pending_events = NULL;
430 if (rsvg->pending_tags) {
431 gst_element_found_tags (GST_ELEMENT_CAST (rsvg), rsvg->pending_tags);
432 rsvg->pending_tags = NULL;
435 GST_LOG_OBJECT (rsvg, "image rendered okay");
437 ret = gst_pad_push (rsvg->srcpad, outbuf);
438 if (ret != GST_FLOW_OK)
441 gst_adapter_flush (rsvg->adapter, size);
442 size = gst_adapter_available (rsvg->adapter);
453 gst_rsvg_dec_sink_set_caps (GstPad * pad, GstCaps * caps)
455 GstRsvgDec *rsvg = GST_RSVG_DEC (gst_pad_get_parent (pad));
457 GstStructure *s = gst_caps_get_structure (caps, 0);
459 gst_structure_get_fraction (s, "framerate", &rsvg->fps_n, &rsvg->fps_d);
461 gst_object_unref (rsvg);
467 gst_rsvg_dec_sink_event (GstPad * pad, GstEvent * event)
469 GstRsvgDec *rsvg = GST_RSVG_DEC (gst_pad_get_parent (pad));
470 gboolean res = FALSE;
472 switch (GST_EVENT_TYPE (event)) {
473 case GST_EVENT_NEWSEGMENT:{
476 gint64 start, stop, position;
479 gst_event_parse_new_segment_full (event, &update, &rate, &arate, &fmt,
480 &start, &stop, &position);
482 gst_segment_set_newsegment_full (&rsvg->segment, update, rate, arate,
483 fmt, start, stop, position);
485 if (fmt == GST_FORMAT_TIME) {
486 rsvg->need_newsegment = FALSE;
487 res = gst_pad_push_event (rsvg->srcpad, event);
489 gst_event_unref (event);
495 case GST_EVENT_FLUSH_STOP:
496 gst_adapter_clear (rsvg->adapter);
498 case GST_EVENT_FLUSH_START:
499 res = gst_pad_push_event (rsvg->srcpad, event);
502 if (GST_PAD_CAPS (rsvg->srcpad)) {
503 res = gst_pad_push_event (rsvg->srcpad, event);
506 rsvg->pending_events = g_list_append (rsvg->pending_events, event);
511 gst_object_unref (rsvg);
517 gst_rsvg_dec_src_event (GstPad * pad, GstEvent * event)
519 GstRsvgDec *rsvg = GST_RSVG_DEC (gst_pad_get_parent (pad));
520 gboolean res = FALSE;
522 switch (GST_EVENT_TYPE (event)) {
524 res = gst_pad_push_event (rsvg->sinkpad, event);
528 gst_object_unref (rsvg);
533 static const GstQueryType *
534 gst_rsvg_dec_src_query_type (GstPad * pad)
536 static const GstQueryType query_types[] = {
544 gst_rsvg_dec_src_query (GstPad * pad, GstQuery * query)
546 GstRsvgDec *rsvg = GST_RSVG_DEC (gst_pad_get_parent (pad));
549 switch (GST_QUERY_TYPE (query)) {
551 res = gst_pad_query_default (pad, query);
555 gst_object_unref (rsvg);
561 gst_rsvg_dec_src_set_caps (GstPad * pad, GstCaps * caps)
563 GstRsvgDec *rsvg = GST_RSVG_DEC (gst_pad_get_parent (pad));
565 GstStructure *s = gst_caps_get_structure (caps, 0);
567 ret &= gst_structure_get_int (s, "width", &rsvg->width);
568 ret &= gst_structure_get_int (s, "height", &rsvg->height);
570 gst_object_unref (rsvg);
575 static GstStateChangeReturn
576 gst_rsvg_dec_change_state (GstElement * element, GstStateChange transition)
578 GstStateChangeReturn res;
579 GstRsvgDec *dec = GST_RSVG_DEC (element);
581 switch (transition) {
582 case GST_STATE_CHANGE_READY_TO_PAUSED:
588 res = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
589 if (res == GST_STATE_CHANGE_FAILURE)
592 switch (transition) {
593 case GST_STATE_CHANGE_PAUSED_TO_READY:
594 gst_rsvg_dec_reset (dec);