• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 GST_ELEMENT_REGISTER_DEFINE (rsvgdec, "rsvgdec", GST_RANK_PRIMARY,
63     GST_TYPE_RSVG_DEC);
64 
65 static gboolean gst_rsvg_dec_stop (GstVideoDecoder * decoder);
66 static gboolean gst_rsvg_dec_set_format (GstVideoDecoder * decoder,
67     GstVideoCodecState * state);
68 static GstFlowReturn gst_rsvg_dec_parse (GstVideoDecoder * decoder,
69     GstVideoCodecFrame * frame, GstAdapter * adapter, gboolean at_eos);
70 static GstFlowReturn gst_rsvg_dec_handle_frame (GstVideoDecoder * decoder,
71     GstVideoCodecFrame * frame);
72 static GstFlowReturn gst_rsvg_decode_image (GstRsvgDec * rsvg,
73     GstBuffer * buffer, GstVideoCodecFrame * frame);
74 
75 static void gst_rsvg_dec_finalize (GObject * object);
76 
77 static void
gst_rsvg_dec_class_init(GstRsvgDecClass * klass)78 gst_rsvg_dec_class_init (GstRsvgDecClass * klass)
79 {
80   GstVideoDecoderClass *video_decoder_class = GST_VIDEO_DECODER_CLASS (klass);
81   GObjectClass *gobject_class = (GObjectClass *) klass;
82   GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
83 
84   GST_DEBUG_CATEGORY_INIT (rsvgdec_debug, "rsvgdec", 0, "RSVG decoder");
85 
86   gst_element_class_set_static_metadata (element_class,
87       "SVG image decoder", "Codec/Decoder/Image",
88       "Uses librsvg to decode SVG images",
89       "Sebastian Dröge <sebastian.droege@collabora.co.uk>");
90 
91   gst_element_class_add_static_pad_template (element_class, &sink_factory);
92   gst_element_class_add_static_pad_template (element_class, &src_factory);
93 
94   gobject_class->finalize = gst_rsvg_dec_finalize;
95   video_decoder_class->stop = GST_DEBUG_FUNCPTR (gst_rsvg_dec_stop);
96   video_decoder_class->set_format = GST_DEBUG_FUNCPTR (gst_rsvg_dec_set_format);
97   video_decoder_class->parse = GST_DEBUG_FUNCPTR (gst_rsvg_dec_parse);
98   video_decoder_class->handle_frame =
99       GST_DEBUG_FUNCPTR (gst_rsvg_dec_handle_frame);
100 }
101 
102 static void
gst_rsvg_dec_init(GstRsvgDec * rsvg)103 gst_rsvg_dec_init (GstRsvgDec * rsvg)
104 {
105   GstVideoDecoder *decoder = GST_VIDEO_DECODER (rsvg);
106   gst_video_decoder_set_packetized (decoder, FALSE);
107   gst_video_decoder_set_use_default_pad_acceptcaps (GST_VIDEO_DECODER_CAST
108       (rsvg), TRUE);
109   GST_PAD_SET_ACCEPT_TEMPLATE (GST_VIDEO_DECODER_SINK_PAD (rsvg));
110 }
111 
112 static void
gst_rsvg_dec_finalize(GObject * object)113 gst_rsvg_dec_finalize (GObject * object)
114 {
115   G_OBJECT_CLASS (gst_rsvg_dec_parent_class)->finalize (object);
116 }
117 
118 
119 #define CAIRO_UNPREMULTIPLY(a,r,g,b) G_STMT_START { \
120   b = (a > 0) ? MIN ((b * 255 + a / 2) / a, 255) : 0; \
121   g = (a > 0) ? MIN ((g * 255 + a / 2) / a, 255) : 0; \
122   r = (a > 0) ? MIN ((r * 255 + a / 2) / a, 255) : 0; \
123 } G_STMT_END
124 
125 static void
gst_rsvg_decode_unpremultiply(guint8 * data,gint width,gint height)126 gst_rsvg_decode_unpremultiply (guint8 * data, gint width, gint height)
127 {
128   gint i, j;
129   guint a;
130 
131   for (i = 0; i < height; i++) {
132     for (j = 0; j < width; j++) {
133 #if G_BYTE_ORDER == G_LITTLE_ENDIAN
134       a = data[3];
135       data[0] = (a > 0) ? MIN ((data[0] * 255 + a / 2) / a, 255) : 0;
136       data[1] = (a > 0) ? MIN ((data[1] * 255 + a / 2) / a, 255) : 0;
137       data[2] = (a > 0) ? MIN ((data[2] * 255 + a / 2) / a, 255) : 0;
138 #else
139       a = data[0];
140       data[1] = (a > 0) ? MIN ((data[1] * 255 + a / 2) / a, 255) : 0;
141       data[2] = (a > 0) ? MIN ((data[2] * 255 + a / 2) / a, 255) : 0;
142       data[3] = (a > 0) ? MIN ((data[3] * 255 + a / 2) / a, 255) : 0;
143 #endif
144       data += 4;
145     }
146   }
147 }
148 
149 static GstFlowReturn
gst_rsvg_decode_image(GstRsvgDec * rsvg,GstBuffer * buffer,GstVideoCodecFrame * frame)150 gst_rsvg_decode_image (GstRsvgDec * rsvg, GstBuffer * buffer,
151     GstVideoCodecFrame * frame)
152 {
153   GstVideoDecoder *decoder = GST_VIDEO_DECODER (rsvg);
154   GstFlowReturn ret = GST_FLOW_OK;
155   cairo_t *cr;
156   cairo_surface_t *surface;
157   RsvgHandle *handle;
158   GError *error = NULL;
159   RsvgDimensionData dimension;
160   gdouble scalex, scaley;
161   GstMapInfo minfo;
162   GstVideoFrame vframe;
163   GstVideoCodecState *output_state;
164 
165   GST_LOG_OBJECT (rsvg, "parsing svg");
166 
167   if (!gst_buffer_map (buffer, &minfo, GST_MAP_READ)) {
168     GST_ERROR_OBJECT (rsvg, "Failed to get SVG image");
169     return GST_FLOW_ERROR;
170   }
171   handle = rsvg_handle_new_from_data (minfo.data, minfo.size, &error);
172   if (!handle) {
173     GST_ERROR_OBJECT (rsvg, "Failed to parse SVG image: %s", error->message);
174     g_error_free (error);
175     return GST_FLOW_ERROR;
176   }
177 
178   rsvg_handle_get_dimensions (handle, &dimension);
179 
180   output_state = gst_video_decoder_get_output_state (decoder);
181   if ((output_state == NULL)
182       || GST_VIDEO_INFO_WIDTH (&output_state->info) != dimension.width
183       || GST_VIDEO_INFO_HEIGHT (&output_state->info) != dimension.height) {
184 
185     /* Create the output state */
186     if (output_state)
187       gst_video_codec_state_unref (output_state);
188     output_state =
189         gst_video_decoder_set_output_state (decoder, GST_RSVG_VIDEO_FORMAT,
190         dimension.width, dimension.height, rsvg->input_state);
191   }
192 
193   ret = gst_video_decoder_allocate_output_frame (decoder, frame);
194 
195   if (ret != GST_FLOW_OK) {
196     g_object_unref (handle);
197     gst_video_codec_state_unref (output_state);
198     GST_ERROR_OBJECT (rsvg, "Buffer allocation failed %s",
199         gst_flow_get_name (ret));
200     return ret;
201   }
202 
203   GST_LOG_OBJECT (rsvg, "render image at %d x %d",
204       GST_VIDEO_INFO_HEIGHT (&output_state->info),
205       GST_VIDEO_INFO_WIDTH (&output_state->info));
206 
207 
208   if (!gst_video_frame_map (&vframe,
209           &output_state->info, frame->output_buffer, GST_MAP_READWRITE)) {
210     GST_ERROR_OBJECT (rsvg, "Failed to get SVG image");
211     g_object_unref (handle);
212     gst_video_codec_state_unref (output_state);
213     return GST_FLOW_ERROR;
214   }
215   surface =
216       cairo_image_surface_create_for_data (GST_VIDEO_FRAME_PLANE_DATA (&vframe,
217           0), CAIRO_FORMAT_ARGB32, GST_VIDEO_FRAME_WIDTH (&vframe),
218       GST_VIDEO_FRAME_HEIGHT (&vframe), GST_VIDEO_FRAME_PLANE_STRIDE (&vframe,
219           0));
220 
221   cr = cairo_create (surface);
222   cairo_set_operator (cr, CAIRO_OPERATOR_CLEAR);
223   cairo_set_source_rgba (cr, 1.0, 1.0, 1.0, 0.0);
224   cairo_paint (cr);
225   cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
226   cairo_set_source_rgba (cr, 0.0, 0.0, 0.0, 1.0);
227 
228   scalex = scaley = 1.0;
229   if (GST_VIDEO_INFO_WIDTH (&output_state->info) != dimension.width) {
230     scalex =
231         ((gdouble) GST_VIDEO_INFO_WIDTH (&output_state->info)) /
232         ((gdouble) dimension.width);
233   }
234   if (GST_VIDEO_INFO_HEIGHT (&output_state->info) != dimension.height) {
235     scaley =
236         ((gdouble) GST_VIDEO_INFO_HEIGHT (&output_state->info)) /
237         ((gdouble) dimension.height);
238   }
239   cairo_scale (cr, scalex, scaley);
240   rsvg_handle_render_cairo (handle, cr);
241 
242   g_object_unref (handle);
243   cairo_destroy (cr);
244   cairo_surface_destroy (surface);
245 
246   /* Now unpremultiply Cairo's ARGB to match GStreamer's */
247   gst_rsvg_decode_unpremultiply (GST_VIDEO_FRAME_PLANE_DATA (&vframe, 0),
248       GST_VIDEO_FRAME_WIDTH (&vframe), GST_VIDEO_FRAME_HEIGHT (&vframe));
249 
250   gst_video_codec_state_unref (output_state);
251   gst_buffer_unmap (buffer, &minfo);
252   gst_video_frame_unmap (&vframe);
253 
254   return ret;
255 }
256 
257 
258 static gboolean
gst_rsvg_dec_set_format(GstVideoDecoder * decoder,GstVideoCodecState * state)259 gst_rsvg_dec_set_format (GstVideoDecoder * decoder, GstVideoCodecState * state)
260 {
261   GstRsvgDec *rsvg = GST_RSVG_DEC (decoder);
262   GstVideoInfo *info = &state->info;
263 
264   if (rsvg->input_state)
265     gst_video_codec_state_unref (rsvg->input_state);
266   rsvg->input_state = gst_video_codec_state_ref (state);
267 
268   /* Create the output state */
269   state = gst_video_decoder_set_output_state (decoder, GST_RSVG_VIDEO_FORMAT,
270       GST_VIDEO_INFO_WIDTH (info), GST_VIDEO_INFO_HEIGHT (info),
271       rsvg->input_state);
272   gst_video_codec_state_unref (state);
273 
274   return TRUE;
275 }
276 
277 static GstFlowReturn
gst_rsvg_dec_parse(GstVideoDecoder * decoder,GstVideoCodecFrame * frame,GstAdapter * adapter,gboolean at_eos)278 gst_rsvg_dec_parse (GstVideoDecoder * decoder, GstVideoCodecFrame * frame,
279     GstAdapter * adapter, gboolean at_eos)
280 {
281   gboolean completed = FALSE;
282   const guint8 *data;
283   guint size;
284   guint i;
285 
286   GST_LOG_OBJECT (decoder, "parse start");
287   size = gst_adapter_available (adapter);
288 
289   /* "<svg></svg>" */
290   if (size < 5 + 6)
291     return GST_VIDEO_DECODER_FLOW_NEED_DATA;
292 
293   data = gst_adapter_map (adapter, size);
294   if (data == NULL) {
295     GST_ERROR_OBJECT (decoder, "Unable to map memory");
296     return GST_FLOW_ERROR;
297   }
298   for (i = 0; i < size - 4; i++) {
299     if (memcmp (data + i, "<svg", 4) == 0) {
300       gst_adapter_flush (adapter, i);
301 
302       size = gst_adapter_available (adapter);
303       if (size < 5 + 6)
304         return GST_VIDEO_DECODER_FLOW_NEED_DATA;
305       data = gst_adapter_map (adapter, size);
306       if (data == NULL) {
307         GST_ERROR_OBJECT (decoder, "Unable to map memory");
308         return GST_FLOW_ERROR;
309       }
310       break;
311     }
312   }
313   /* If start wasn't found: */
314   if (i == size - 4) {
315     gst_adapter_flush (adapter, size - 4);
316     return GST_VIDEO_DECODER_FLOW_NEED_DATA;
317   }
318 
319   for (i = size - 6; i >= 5; i--) {
320     if (memcmp (data + i, "</svg>", 6) == 0) {
321       completed = TRUE;
322       size = i + 6;
323       break;
324     }
325     if (memcmp (data + i, "</svg:svg>", 10) == 0) {
326       completed = TRUE;
327       size = i + 10;
328       break;
329     }
330   }
331 
332   if (completed) {
333 
334     GST_LOG_OBJECT (decoder, "have complete svg of %u bytes", size);
335 
336     gst_video_decoder_add_to_frame (decoder, size);
337     return gst_video_decoder_have_frame (decoder);
338   }
339   return GST_VIDEO_DECODER_FLOW_NEED_DATA;
340 }
341 
342 static GstFlowReturn
gst_rsvg_dec_handle_frame(GstVideoDecoder * decoder,GstVideoCodecFrame * frame)343 gst_rsvg_dec_handle_frame (GstVideoDecoder * decoder,
344     GstVideoCodecFrame * frame)
345 {
346   GstRsvgDec *rsvg = GST_RSVG_DEC (decoder);
347   gboolean ret;
348 
349   ret = gst_rsvg_decode_image (rsvg, frame->input_buffer, frame);
350   switch (ret) {
351     case GST_FLOW_OK:
352       ret = gst_video_decoder_finish_frame (decoder, frame);
353       break;
354     default:
355       gst_video_codec_frame_unref (frame);
356       break;
357   }
358 
359   GST_LOG_OBJECT (rsvg, "Handle frame done");
360   return ret;
361 }
362 
363 static gboolean
gst_rsvg_dec_stop(GstVideoDecoder * decoder)364 gst_rsvg_dec_stop (GstVideoDecoder * decoder)
365 {
366   GstRsvgDec *rsvg = GST_RSVG_DEC (decoder);
367 
368   if (rsvg->input_state) {
369     gst_video_codec_state_unref (rsvg->input_state);
370     rsvg->input_state = NULL;
371   }
372 
373   return TRUE;
374 }
375