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