1 /* GStreamer
2  * Copyright (C) <2009> Sebastian Dröge <sebastian.droege@collabora.co.uk>
3  * Copyright (C) <2013> Luciana Fujii <luciana.fujii@collabora.co.uk>
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Library General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Library General Public License for more details.
14  *
15  * You should have received a copy of the GNU Library General Public
16  * License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
18  * Boston, MA 02110-1301, USA.
19  */
20 /**
21  * SECTION:element-rsvgdec
22  * @title: rsvgdec
23  *
24  * This elements renders SVG graphics.
25  *
26  * ## Example launch lines
27  * |[
28  * gst-launch-1.0 filesrc location=image.svg ! rsvgdec ! imagefreeze ! videoconvert ! autovideosink
29  * ]| render and show a svg image.
30  *
31  */
32 
33 #ifdef HAVE_CONFIG_H
34 #include "config.h"
35 #endif
36 
37 #include "gstrsvgdec.h"
38 
39 #include <string.h>
40 
41 GST_DEBUG_CATEGORY_STATIC (rsvgdec_debug);
42 #define GST_CAT_DEFAULT rsvgdec_debug
43 
44 static GstStaticPadTemplate sink_factory =
45     GST_STATIC_PAD_TEMPLATE ("sink", GST_PAD_SINK, GST_PAD_ALWAYS,
46     GST_STATIC_CAPS ("image/svg+xml; image/svg"));
47 
48 #if G_BYTE_ORDER == G_LITTLE_ENDIAN
49 #define GST_RSVG_VIDEO_CAPS GST_VIDEO_CAPS_MAKE ("BGRA")
50 #define GST_RSVG_VIDEO_FORMAT GST_VIDEO_FORMAT_BGRA
51 #else
52 #define GST_RSVG_VIDEO_CAPS GST_VIDEO_CAPS_MAKE ("ARGB")
53 #define GST_RSVG_VIDEO_FORMAT GST_VIDEO_FORMAT_ARGB
54 #endif
55 
56 static GstStaticPadTemplate src_factory =
57 GST_STATIC_PAD_TEMPLATE ("src", GST_PAD_SRC, GST_PAD_ALWAYS,
58     GST_STATIC_CAPS (GST_RSVG_VIDEO_CAPS));
59 
60 #define gst_rsv_dec_parent_class parent_class
61 G_DEFINE_TYPE (GstRsvgDec, gst_rsvg_dec, GST_TYPE_VIDEO_DECODER);
62 
63 static gboolean gst_rsvg_dec_stop (GstVideoDecoder * decoder);
64 static gboolean gst_rsvg_dec_set_format (GstVideoDecoder * decoder,
65     GstVideoCodecState * state);
66 static GstFlowReturn gst_rsvg_dec_parse (GstVideoDecoder * decoder,
67     GstVideoCodecFrame * frame, GstAdapter * adapter, gboolean at_eos);
68 static GstFlowReturn gst_rsvg_dec_handle_frame (GstVideoDecoder * decoder,
69     GstVideoCodecFrame * frame);
70 static GstFlowReturn gst_rsvg_decode_image (GstRsvgDec * rsvg,
71     GstBuffer * buffer, GstVideoCodecFrame * frame);
72 
73 static void gst_rsvg_dec_finalize (GObject * object);
74 
75 static void
gst_rsvg_dec_class_init(GstRsvgDecClass * klass)76 gst_rsvg_dec_class_init (GstRsvgDecClass * klass)
77 {
78   GstVideoDecoderClass *video_decoder_class = GST_VIDEO_DECODER_CLASS (klass);
79   GObjectClass *gobject_class = (GObjectClass *) klass;
80   GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
81 
82   GST_DEBUG_CATEGORY_INIT (rsvgdec_debug, "rsvgdec", 0, "RSVG decoder");
83 
84   gst_element_class_set_static_metadata (element_class,
85       "SVG image decoder", "Codec/Decoder/Image",
86       "Uses librsvg to decode SVG images",
87       "Sebastian Dröge <sebastian.droege@collabora.co.uk>");
88 
89   gst_element_class_add_static_pad_template (element_class, &sink_factory);
90   gst_element_class_add_static_pad_template (element_class, &src_factory);
91 
92   gobject_class->finalize = gst_rsvg_dec_finalize;
93   video_decoder_class->stop = GST_DEBUG_FUNCPTR (gst_rsvg_dec_stop);
94   video_decoder_class->set_format = GST_DEBUG_FUNCPTR (gst_rsvg_dec_set_format);
95   video_decoder_class->parse = GST_DEBUG_FUNCPTR (gst_rsvg_dec_parse);
96   video_decoder_class->handle_frame =
97       GST_DEBUG_FUNCPTR (gst_rsvg_dec_handle_frame);
98 }
99 
100 static void
gst_rsvg_dec_init(GstRsvgDec * rsvg)101 gst_rsvg_dec_init (GstRsvgDec * rsvg)
102 {
103   GstVideoDecoder *decoder = GST_VIDEO_DECODER (rsvg);
104   gst_video_decoder_set_packetized (decoder, FALSE);
105   gst_video_decoder_set_use_default_pad_acceptcaps (GST_VIDEO_DECODER_CAST
106       (rsvg), TRUE);
107   GST_PAD_SET_ACCEPT_TEMPLATE (GST_VIDEO_DECODER_SINK_PAD (rsvg));
108 }
109 
110 static void
gst_rsvg_dec_finalize(GObject * object)111 gst_rsvg_dec_finalize (GObject * object)
112 {
113   G_OBJECT_CLASS (gst_rsvg_dec_parent_class)->finalize (object);
114 }
115 
116 
117 #define CAIRO_UNPREMULTIPLY(a,r,g,b) G_STMT_START { \
118   b = (a > 0) ? MIN ((b * 255 + a / 2) / a, 255) : 0; \
119   g = (a > 0) ? MIN ((g * 255 + a / 2) / a, 255) : 0; \
120   r = (a > 0) ? MIN ((r * 255 + a / 2) / a, 255) : 0; \
121 } G_STMT_END
122 
123 static void
gst_rsvg_decode_unpremultiply(guint8 * data,gint width,gint height)124 gst_rsvg_decode_unpremultiply (guint8 * data, gint width, gint height)
125 {
126   gint i, j;
127   guint a;
128 
129   for (i = 0; i < height; i++) {
130     for (j = 0; j < width; j++) {
131 #if G_BYTE_ORDER == G_LITTLE_ENDIAN
132       a = data[3];
133       data[0] = (a > 0) ? MIN ((data[0] * 255 + a / 2) / a, 255) : 0;
134       data[1] = (a > 0) ? MIN ((data[1] * 255 + a / 2) / a, 255) : 0;
135       data[2] = (a > 0) ? MIN ((data[2] * 255 + a / 2) / a, 255) : 0;
136 #else
137       a = data[0];
138       data[1] = (a > 0) ? MIN ((data[1] * 255 + a / 2) / a, 255) : 0;
139       data[2] = (a > 0) ? MIN ((data[2] * 255 + a / 2) / a, 255) : 0;
140       data[3] = (a > 0) ? MIN ((data[3] * 255 + a / 2) / a, 255) : 0;
141 #endif
142       data += 4;
143     }
144   }
145 }
146 
147 static GstFlowReturn
gst_rsvg_decode_image(GstRsvgDec * rsvg,GstBuffer * buffer,GstVideoCodecFrame * frame)148 gst_rsvg_decode_image (GstRsvgDec * rsvg, GstBuffer * buffer,
149     GstVideoCodecFrame * frame)
150 {
151   GstVideoDecoder *decoder = GST_VIDEO_DECODER (rsvg);
152   GstFlowReturn ret = GST_FLOW_OK;
153   cairo_t *cr;
154   cairo_surface_t *surface;
155   RsvgHandle *handle;
156   GError *error = NULL;
157   RsvgDimensionData dimension;
158   gdouble scalex, scaley;
159   GstMapInfo minfo;
160   GstVideoFrame vframe;
161   GstVideoCodecState *output_state;
162 
163   GST_LOG_OBJECT (rsvg, "parsing svg");
164 
165   if (!gst_buffer_map (buffer, &minfo, GST_MAP_READ)) {
166     GST_ERROR_OBJECT (rsvg, "Failed to get SVG image");
167     return GST_FLOW_ERROR;
168   }
169   handle = rsvg_handle_new_from_data (minfo.data, minfo.size, &error);
170   if (!handle) {
171     GST_ERROR_OBJECT (rsvg, "Failed to parse SVG image: %s", error->message);
172     g_error_free (error);
173     return GST_FLOW_ERROR;
174   }
175 
176   rsvg_handle_get_dimensions (handle, &dimension);
177 
178   output_state = gst_video_decoder_get_output_state (decoder);
179   if ((output_state == NULL)
180       || GST_VIDEO_INFO_WIDTH (&output_state->info) != dimension.width
181       || GST_VIDEO_INFO_HEIGHT (&output_state->info) != dimension.height) {
182 
183     /* Create the output state */
184     if (output_state)
185       gst_video_codec_state_unref (output_state);
186     output_state =
187         gst_video_decoder_set_output_state (decoder, GST_RSVG_VIDEO_FORMAT,
188         dimension.width, dimension.height, rsvg->input_state);
189   }
190 
191   ret = gst_video_decoder_allocate_output_frame (decoder, frame);
192 
193   if (ret != GST_FLOW_OK) {
194     g_object_unref (handle);
195     gst_video_codec_state_unref (output_state);
196     GST_ERROR_OBJECT (rsvg, "Buffer allocation failed %s",
197         gst_flow_get_name (ret));
198     return ret;
199   }
200 
201   GST_LOG_OBJECT (rsvg, "render image at %d x %d",
202       GST_VIDEO_INFO_HEIGHT (&output_state->info),
203       GST_VIDEO_INFO_WIDTH (&output_state->info));
204 
205 
206   if (!gst_video_frame_map (&vframe,
207           &output_state->info, frame->output_buffer, GST_MAP_READWRITE)) {
208     GST_ERROR_OBJECT (rsvg, "Failed to get SVG image");
209     g_object_unref (handle);
210     gst_video_codec_state_unref (output_state);
211     return GST_FLOW_ERROR;
212   }
213   surface =
214       cairo_image_surface_create_for_data (GST_VIDEO_FRAME_PLANE_DATA (&vframe,
215           0), CAIRO_FORMAT_ARGB32, GST_VIDEO_FRAME_WIDTH (&vframe),
216       GST_VIDEO_FRAME_HEIGHT (&vframe), GST_VIDEO_FRAME_PLANE_STRIDE (&vframe,
217           0));
218 
219   cr = cairo_create (surface);
220   cairo_set_operator (cr, CAIRO_OPERATOR_CLEAR);
221   cairo_set_source_rgba (cr, 1.0, 1.0, 1.0, 0.0);
222   cairo_paint (cr);
223   cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
224   cairo_set_source_rgba (cr, 0.0, 0.0, 0.0, 1.0);
225 
226   scalex = scaley = 1.0;
227   if (GST_VIDEO_INFO_WIDTH (&output_state->info) != dimension.width) {
228     scalex =
229         ((gdouble) GST_VIDEO_INFO_WIDTH (&output_state->info)) /
230         ((gdouble) dimension.width);
231   }
232   if (GST_VIDEO_INFO_HEIGHT (&output_state->info) != dimension.height) {
233     scaley =
234         ((gdouble) GST_VIDEO_INFO_HEIGHT (&output_state->info)) /
235         ((gdouble) dimension.height);
236   }
237   cairo_scale (cr, scalex, scaley);
238   rsvg_handle_render_cairo (handle, cr);
239 
240   g_object_unref (handle);
241   cairo_destroy (cr);
242   cairo_surface_destroy (surface);
243 
244   /* Now unpremultiply Cairo's ARGB to match GStreamer's */
245   gst_rsvg_decode_unpremultiply (GST_VIDEO_FRAME_PLANE_DATA (&vframe, 0),
246       GST_VIDEO_FRAME_WIDTH (&vframe), GST_VIDEO_FRAME_HEIGHT (&vframe));
247 
248   gst_video_codec_state_unref (output_state);
249   gst_buffer_unmap (buffer, &minfo);
250   gst_video_frame_unmap (&vframe);
251 
252   return ret;
253 }
254 
255 
256 static gboolean
gst_rsvg_dec_set_format(GstVideoDecoder * decoder,GstVideoCodecState * state)257 gst_rsvg_dec_set_format (GstVideoDecoder * decoder, GstVideoCodecState * state)
258 {
259   GstRsvgDec *rsvg = GST_RSVG_DEC (decoder);
260   GstVideoInfo *info = &state->info;
261 
262   if (rsvg->input_state)
263     gst_video_codec_state_unref (rsvg->input_state);
264   rsvg->input_state = gst_video_codec_state_ref (state);
265 
266   /* Create the output state */
267   state = gst_video_decoder_set_output_state (decoder, GST_RSVG_VIDEO_FORMAT,
268       GST_VIDEO_INFO_WIDTH (info), GST_VIDEO_INFO_HEIGHT (info),
269       rsvg->input_state);
270   gst_video_codec_state_unref (state);
271 
272   return TRUE;
273 }
274 
275 static GstFlowReturn
gst_rsvg_dec_parse(GstVideoDecoder * decoder,GstVideoCodecFrame * frame,GstAdapter * adapter,gboolean at_eos)276 gst_rsvg_dec_parse (GstVideoDecoder * decoder, GstVideoCodecFrame * frame,
277     GstAdapter * adapter, gboolean at_eos)
278 {
279   gboolean completed = FALSE;
280   const guint8 *data;
281   guint size;
282   guint i;
283 
284   GST_LOG_OBJECT (decoder, "parse start");
285   size = gst_adapter_available (adapter);
286 
287   /* "<svg></svg>" */
288   if (size < 5 + 6)
289     return GST_VIDEO_DECODER_FLOW_NEED_DATA;
290 
291   data = gst_adapter_map (adapter, size);
292   if (data == NULL) {
293     GST_ERROR_OBJECT (decoder, "Unable to map memory");
294     return GST_FLOW_ERROR;
295   }
296   for (i = 0; i < size - 4; i++) {
297     if (memcmp (data + i, "<svg", 4) == 0) {
298       gst_adapter_flush (adapter, i);
299 
300       size = gst_adapter_available (adapter);
301       if (size < 5 + 6)
302         return GST_VIDEO_DECODER_FLOW_NEED_DATA;
303       data = gst_adapter_map (adapter, size);
304       if (data == NULL) {
305         GST_ERROR_OBJECT (decoder, "Unable to map memory");
306         return GST_FLOW_ERROR;
307       }
308       break;
309     }
310   }
311   /* If start wasn't found: */
312   if (i == size - 4) {
313     gst_adapter_flush (adapter, size - 4);
314     return GST_VIDEO_DECODER_FLOW_NEED_DATA;
315   }
316 
317   for (i = size - 6; i >= 5; i--) {
318     if (memcmp (data + i, "</svg>", 6) == 0) {
319       completed = TRUE;
320       size = i + 6;
321       break;
322     }
323     if (memcmp (data + i, "</svg:svg>", 10) == 0) {
324       completed = TRUE;
325       size = i + 10;
326       break;
327     }
328   }
329 
330   if (completed) {
331 
332     GST_LOG_OBJECT (decoder, "have complete svg of %u bytes", size);
333 
334     gst_video_decoder_add_to_frame (decoder, size);
335     return gst_video_decoder_have_frame (decoder);
336   }
337   return GST_VIDEO_DECODER_FLOW_NEED_DATA;
338 }
339 
340 static GstFlowReturn
gst_rsvg_dec_handle_frame(GstVideoDecoder * decoder,GstVideoCodecFrame * frame)341 gst_rsvg_dec_handle_frame (GstVideoDecoder * decoder,
342     GstVideoCodecFrame * frame)
343 {
344   GstRsvgDec *rsvg = GST_RSVG_DEC (decoder);
345   gboolean ret;
346 
347   ret = gst_rsvg_decode_image (rsvg, frame->input_buffer, frame);
348   switch (ret) {
349     case GST_FLOW_OK:
350       ret = gst_video_decoder_finish_frame (decoder, frame);
351       break;
352     default:
353       gst_video_codec_frame_unref (frame);
354       break;
355   }
356 
357   GST_LOG_OBJECT (rsvg, "Handle frame done");
358   return ret;
359 }
360 
361 static gboolean
gst_rsvg_dec_stop(GstVideoDecoder * decoder)362 gst_rsvg_dec_stop (GstVideoDecoder * decoder)
363 {
364   GstRsvgDec *rsvg = GST_RSVG_DEC (decoder);
365 
366   if (rsvg->input_state) {
367     gst_video_codec_state_unref (rsvg->input_state);
368     rsvg->input_state = NULL;
369   }
370 
371   return TRUE;
372 }
373