1cecdab67debc77edfcf9323ef2da39971c5c027
[platform/upstream/gst-plugins-good.git] / ext / gdk_pixbuf / pixbufscale.c
1 /* GStreamer
2  * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
3  * Copyright (C) <2004> Jan Schmidt <thaytan@mad.scientist.com>
4  * Copyright (C) <2004> Tim-Philipp Mueller <t.i.m@orange.net>
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Library General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Library General Public License for more details.
15  *
16  * You should have received a copy of the GNU Library General Public
17  * License along with this library; if not, write to the
18  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19  * Boston, MA 02111-1307, USA.
20  */
21
22
23 #ifdef HAVE_CONFIG_H
24 #include "config.h"
25 #endif
26 #include <pixbufscale.h>
27 #include <gst/video/video.h>
28 #include <gdk-pixbuf/gdk-pixbuf.h>
29
30 #define ROUND_UP_2(x)  (((x)+1)&~1)
31 #define ROUND_UP_4(x)  (((x)+3)&~3)
32 #define ROUND_UP_8(x)  (((x)+7)&~7)
33
34 /* These match the ones gstffmpegcolorspace uses (Tim) */
35 #define GST_RGB24_ROWSTRIDE(width)    (ROUND_UP_4 ((width)*3))
36 #define GST_RGB24_SIZE(width,height)  ((height)*GST_RGB24_ROWSTRIDE(width))
37
38
39 /* debug variable definition */
40 GST_DEBUG_CATEGORY (pixbufscale_debug);
41 #define GST_CAT_DEFAULT pixbufscale_debug
42
43 /* elementfactory information */
44 static GstElementDetails pixbufscale_details =
45 GST_ELEMENT_DETAILS ("Gdk Pixbuf scaler",
46     "Filter/Effect/Video",
47     "Resizes video",
48     "Jan Schmidt <thaytan@mad.scientist.com>\nWim Taymans <wim.taymans@chello.be>");
49
50 /* GstPixbufScale signals and args */
51 enum
52 {
53   /* FILL ME */
54   LAST_SIGNAL
55 };
56
57 enum
58 {
59   ARG_0,
60   ARG_METHOD
61       /* FILL ME */
62 };
63
64 static GstStaticPadTemplate gst_pixbufscale_src_template =
65 GST_STATIC_PAD_TEMPLATE ("src",
66     GST_PAD_SRC,
67     GST_PAD_ALWAYS,
68     GST_STATIC_CAPS (GST_VIDEO_CAPS_RGB)
69     );
70
71 static GstStaticPadTemplate gst_pixbufscale_sink_template =
72 GST_STATIC_PAD_TEMPLATE ("sink",
73     GST_PAD_SINK,
74     GST_PAD_ALWAYS,
75     GST_STATIC_CAPS (GST_VIDEO_CAPS_RGB)
76     );
77
78 #define GST_TYPE_PIXBUFSCALE_METHOD (gst_pixbufscale_method_get_type())
79 static GType
80 gst_pixbufscale_method_get_type (void)
81 {
82   static GType pixbufscale_method_type = 0;
83   static GEnumValue pixbufscale_methods[] = {
84     {GST_PIXBUFSCALE_NEAREST, "0", "Nearest Neighbour"},
85     {GST_PIXBUFSCALE_TILES, "1", "Tiles"},
86     {GST_PIXBUFSCALE_BILINEAR, "2", "Bilinear"},
87     {GST_PIXBUFSCALE_HYPER, "3", "Hyper"},
88     {0, NULL, NULL},
89   };
90
91   if (!pixbufscale_method_type) {
92     pixbufscale_method_type =
93         g_enum_register_static ("GstPixbufScaleMethod", pixbufscale_methods);
94   }
95   return pixbufscale_method_type;
96 }
97
98 static void gst_pixbufscale_base_init (gpointer g_class);
99 static void gst_pixbufscale_class_init (GstPixbufScaleClass * klass);
100 static void gst_pixbufscale_init (GstPixbufScale * pixbufscale);
101 static gboolean gst_pixbufscale_handle_src_event (GstPad * pad,
102     GstEvent * event);
103
104 static void gst_pixbufscale_set_property (GObject * object, guint prop_id,
105     const GValue * value, GParamSpec * pspec);
106 static void gst_pixbufscale_get_property (GObject * object, guint prop_id,
107     GValue * value, GParamSpec * pspec);
108
109 static void gst_pixbufscale_chain (GstPad * pad, GstData * _data);
110
111 static GstElementClass *parent_class = NULL;
112
113 GType
114 gst_pixbufscale_get_type (void)
115 {
116   static GType pixbufscale_type = 0;
117
118   if (!pixbufscale_type) {
119     static const GTypeInfo pixbufscale_info = {
120       sizeof (GstPixbufScaleClass),
121       gst_pixbufscale_base_init,
122       NULL,
123       (GClassInitFunc) gst_pixbufscale_class_init,
124       NULL,
125       NULL,
126       sizeof (GstPixbufScale),
127       0,
128       (GInstanceInitFunc) gst_pixbufscale_init,
129     };
130
131     pixbufscale_type =
132         g_type_register_static (GST_TYPE_ELEMENT, "GstPixbufScale",
133         &pixbufscale_info, 0);
134   }
135   return pixbufscale_type;
136 }
137
138 static void
139 gst_pixbufscale_base_init (gpointer g_class)
140 {
141   GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
142
143   gst_element_class_set_details (element_class, &pixbufscale_details);
144
145   gst_element_class_add_pad_template (element_class,
146       gst_static_pad_template_get (&gst_pixbufscale_src_template));
147   gst_element_class_add_pad_template (element_class,
148       gst_static_pad_template_get (&gst_pixbufscale_sink_template));
149 }
150 static void
151 gst_pixbufscale_class_init (GstPixbufScaleClass * klass)
152 {
153   GObjectClass *gobject_class;
154   GstElementClass *gstelement_class;
155
156   gobject_class = (GObjectClass *) klass;
157   gstelement_class = (GstElementClass *) klass;
158
159   g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_METHOD,
160       g_param_spec_enum ("method", "method", "method",
161           GST_TYPE_PIXBUFSCALE_METHOD, GST_PIXBUFSCALE_BILINEAR,
162           G_PARAM_READWRITE));
163
164   parent_class = g_type_class_ref (GST_TYPE_ELEMENT);
165
166   gobject_class->set_property = gst_pixbufscale_set_property;
167   gobject_class->get_property = gst_pixbufscale_get_property;
168 }
169
170 static GstCaps *
171 gst_pixbufscale_getcaps (GstPad * pad)
172 {
173   GstPixbufScale *pixbufscale;
174   GstCaps *othercaps;
175   GstCaps *caps;
176   GstPad *otherpad;
177   int i;
178
179   pixbufscale = GST_PIXBUFSCALE (gst_pad_get_parent (pad));
180
181   otherpad = (pad == pixbufscale->srcpad) ? pixbufscale->sinkpad :
182       pixbufscale->srcpad;
183   othercaps = gst_pad_get_allowed_caps (otherpad);
184
185   caps = gst_caps_intersect (othercaps, gst_pad_get_pad_template_caps (pad));
186   gst_caps_free (othercaps);
187
188   for (i = 0; i < gst_caps_get_size (caps); i++) {
189     GstStructure *structure = gst_caps_get_structure (caps, i);
190
191     gst_structure_set (structure,
192         "width", GST_TYPE_INT_RANGE, 16, 4096,
193         "height", GST_TYPE_INT_RANGE, 16, 4096, NULL);
194     gst_structure_remove_field (structure, "pixel-aspect-ratio");
195   }
196
197   GST_DEBUG ("getcaps are: %" GST_PTR_FORMAT, caps);
198   return caps;
199 }
200
201 static GstPadLinkReturn
202 gst_pixbufscale_link (GstPad * pad, const GstCaps * caps)
203 {
204   GstPixbufScale *pixbufscale;
205   GstPadLinkReturn ret;
206   GstPad *otherpad;
207   GstStructure *structure;
208   int height, width;
209   gchar *caps_string;
210
211   caps_string = gst_caps_to_string (caps);
212   GST_DEBUG ("gst_pixbufscale_link %s\n", caps_string);
213   g_free (caps_string);
214
215   pixbufscale = GST_PIXBUFSCALE (gst_pad_get_parent (pad));
216
217   otherpad = (pad == pixbufscale->srcpad) ? pixbufscale->sinkpad :
218       pixbufscale->srcpad;
219
220   structure = gst_caps_get_structure (caps, 0);
221   ret = gst_structure_get_int (structure, "width", &width);
222   ret &= gst_structure_get_int (structure, "height", &height);
223
224   ret = gst_pad_try_set_caps (otherpad, caps);
225   if (ret == GST_PAD_LINK_OK) {
226     /* cool, we can use passthru */
227
228     pixbufscale->to_width = width;
229     pixbufscale->to_height = height;
230     pixbufscale->from_width = width;
231     pixbufscale->from_height = height;
232
233     pixbufscale->from_buf_size = GST_RGB24_SIZE (width, height);
234     pixbufscale->to_buf_size = GST_RGB24_SIZE (width, height);
235     pixbufscale->from_stride = GST_RGB24_ROWSTRIDE (width);
236     pixbufscale->to_stride = GST_RGB24_ROWSTRIDE (width);
237
238     pixbufscale->inited = TRUE;
239
240     return GST_PAD_LINK_OK;
241   }
242
243   if (gst_pad_is_negotiated (otherpad)) {
244     GstCaps *newcaps = gst_caps_copy (caps);
245
246     if (pad == pixbufscale->srcpad) {
247       gst_caps_set_simple (newcaps,
248           "width", G_TYPE_INT, pixbufscale->from_width,
249           "height", G_TYPE_INT, pixbufscale->from_height, NULL);
250     } else {
251       gst_caps_set_simple (newcaps,
252           "width", G_TYPE_INT, pixbufscale->to_width,
253           "height", G_TYPE_INT, pixbufscale->to_height, NULL);
254     }
255     ret = gst_pad_try_set_caps (otherpad, newcaps);
256     if (GST_PAD_LINK_FAILED (ret)) {
257       return GST_PAD_LINK_REFUSED;
258     }
259   }
260
261   pixbufscale->passthru = FALSE;
262
263   if (pad == pixbufscale->srcpad) {
264     pixbufscale->to_width = width;
265     pixbufscale->to_height = height;
266   } else {
267     pixbufscale->from_width = width;
268     pixbufscale->from_height = height;
269   }
270
271   if (gst_pad_is_negotiated (otherpad)) {
272     pixbufscale->from_buf_size =
273         GST_RGB24_SIZE (pixbufscale->from_width, pixbufscale->from_height);
274     pixbufscale->to_buf_size =
275         GST_RGB24_SIZE (pixbufscale->to_width, pixbufscale->to_height);
276     pixbufscale->from_stride = GST_RGB24_ROWSTRIDE (pixbufscale->from_width);
277     pixbufscale->to_stride = GST_RGB24_ROWSTRIDE (pixbufscale->to_width);
278     pixbufscale->inited = TRUE;
279   }
280
281   return GST_PAD_LINK_OK;
282 }
283
284 static void
285 gst_pixbufscale_init (GstPixbufScale * pixbufscale)
286 {
287   GST_DEBUG_OBJECT (pixbufscale, "_init");
288   pixbufscale->sinkpad =
289       gst_pad_new_from_template (gst_static_pad_template_get
290       (&gst_pixbufscale_sink_template), "sink");
291   gst_element_add_pad (GST_ELEMENT (pixbufscale), pixbufscale->sinkpad);
292   gst_pad_set_chain_function (pixbufscale->sinkpad, gst_pixbufscale_chain);
293   gst_pad_set_link_function (pixbufscale->sinkpad, gst_pixbufscale_link);
294   gst_pad_set_getcaps_function (pixbufscale->sinkpad, gst_pixbufscale_getcaps);
295
296   pixbufscale->srcpad =
297       gst_pad_new_from_template (gst_static_pad_template_get
298       (&gst_pixbufscale_src_template), "src");
299   gst_element_add_pad (GST_ELEMENT (pixbufscale), pixbufscale->srcpad);
300   gst_pad_set_event_function (pixbufscale->srcpad,
301       gst_pixbufscale_handle_src_event);
302   gst_pad_set_link_function (pixbufscale->srcpad, gst_pixbufscale_link);
303   gst_pad_set_getcaps_function (pixbufscale->srcpad, gst_pixbufscale_getcaps);
304
305   pixbufscale->inited = FALSE;
306
307   pixbufscale->method = GST_PIXBUFSCALE_TILES;
308   pixbufscale->gdk_method = GDK_INTERP_TILES;
309 }
310
311 static gboolean
312 gst_pixbufscale_handle_src_event (GstPad * pad, GstEvent * event)
313 {
314   GstPixbufScale *pixbufscale;
315   double a;
316   GstStructure *structure;
317   GstEvent *new_event;
318
319   pixbufscale = GST_PIXBUFSCALE (gst_pad_get_parent (pad));
320
321   switch (GST_EVENT_TYPE (event)) {
322     case GST_EVENT_NAVIGATION:
323       structure = gst_structure_copy (event->event_data.structure.structure);
324       if (gst_structure_get_double (event->event_data.structure.structure,
325               "pointer_x", &a)) {
326         gst_structure_set (structure, "pointer_x", G_TYPE_DOUBLE,
327             a * pixbufscale->from_width / pixbufscale->to_width, NULL);
328       }
329       if (gst_structure_get_double (event->event_data.structure.structure,
330               "pointer_y", &a)) {
331         gst_structure_set (structure, "pointer_y", G_TYPE_DOUBLE,
332             a * pixbufscale->from_height / pixbufscale->to_height, NULL);
333       }
334       gst_event_unref (event);
335       new_event = gst_event_new (GST_EVENT_NAVIGATION);
336       new_event->event_data.structure.structure = structure;
337       return gst_pad_event_default (pad, new_event);
338       break;
339     default:
340       return gst_pad_event_default (pad, event);
341       break;
342   }
343 }
344
345 static void
346 pixbufscale_scale (GstPixbufScale * scale, unsigned char *dest,
347     unsigned char *src)
348 {
349   GdkPixbuf *src_pixbuf, *dest_pixbuf;
350
351   src_pixbuf = gdk_pixbuf_new_from_data
352       (src, GDK_COLORSPACE_RGB, FALSE,
353       8, scale->from_width, scale->from_height,
354       GST_RGB24_ROWSTRIDE (scale->from_width), NULL, NULL);
355   dest_pixbuf = gdk_pixbuf_new_from_data
356       (dest, GDK_COLORSPACE_RGB, FALSE,
357       8, scale->to_width, scale->to_height,
358       GST_RGB24_ROWSTRIDE (scale->to_width), NULL, NULL);
359   gdk_pixbuf_scale (src_pixbuf, dest_pixbuf, 0, 0, scale->to_width,
360       scale->to_height, 0, 0,
361       (double) scale->to_width / scale->from_width,
362       (double) scale->to_height / scale->from_height, scale->gdk_method);
363
364   dest_pixbuf = gdk_pixbuf_scale_simple
365       (src_pixbuf, scale->to_width, scale->to_height, scale->gdk_method);
366
367   g_object_unref (src_pixbuf);
368   g_object_unref (dest_pixbuf);
369 }
370
371 static void
372 gst_pixbufscale_chain (GstPad * pad, GstData * _data)
373 {
374   GstBuffer *buf = GST_BUFFER (_data);
375   GstPixbufScale *pixbufscale;
376   guchar *data;
377   gulong size;
378   GstBuffer *outbuf;
379
380   g_return_if_fail (pad != NULL);
381   g_return_if_fail (GST_IS_PAD (pad));
382   g_return_if_fail (buf != NULL);
383
384   pixbufscale = GST_PIXBUFSCALE (gst_pad_get_parent (pad));
385   g_return_if_fail (pixbufscale->inited);
386
387   data = GST_BUFFER_DATA (buf);
388   size = GST_BUFFER_SIZE (buf);
389
390   if (pixbufscale->passthru) {
391     GST_LOG_OBJECT (pixbufscale, "passing through buffer of %ld bytes in '%s'",
392         size, GST_OBJECT_NAME (pixbufscale));
393     gst_pad_push (pixbufscale->srcpad, GST_DATA (buf));
394     return;
395   }
396
397   GST_LOG_OBJECT (pixbufscale, "got buffer of %ld bytes in '%s'", size,
398       GST_OBJECT_NAME (pixbufscale));
399   GST_LOG_OBJECT (pixbufscale,
400       "size=%ld from=%dx%d to=%dx%d fromsize=%ld (should be %d) tosize=%d",
401       size, pixbufscale->from_width, pixbufscale->from_height,
402       pixbufscale->to_width, pixbufscale->to_height, size,
403       pixbufscale->from_buf_size, pixbufscale->to_buf_size);
404
405   if (size != pixbufscale->from_buf_size) {
406     GST_ERROR ("Incoming RGB data is %d bytes (expected: %d bytes) (%dx%d)\n",
407         size, pixbufscale->from_buf_size, pixbufscale->from_width,
408         pixbufscale->from_height);
409     return;
410   }
411
412   outbuf = gst_pad_alloc_buffer (pixbufscale->srcpad,
413       GST_BUFFER_OFFSET_NONE, pixbufscale->to_buf_size);
414
415   gst_buffer_stamp (outbuf, buf);
416
417   pixbufscale_scale (pixbufscale, GST_BUFFER_DATA (outbuf), data);
418
419   GST_DEBUG_OBJECT (pixbufscale, "pushing buffer of %d bytes in '%s'",
420       GST_BUFFER_SIZE (outbuf), GST_OBJECT_NAME (pixbufscale));
421
422   gst_pad_push (pixbufscale->srcpad, GST_DATA (outbuf));
423
424   gst_buffer_unref (buf);
425 }
426
427 static void
428 gst_pixbufscale_set_property (GObject * object, guint prop_id,
429     const GValue * value, GParamSpec * pspec)
430 {
431   GstPixbufScale *src;
432
433   g_return_if_fail (GST_IS_PIXBUFSCALE (object));
434   src = GST_PIXBUFSCALE (object);
435
436   switch (prop_id) {
437     case ARG_METHOD:
438       src->method = g_value_get_enum (value);
439       switch (src->method) {
440         case GST_PIXBUFSCALE_NEAREST:
441           src->gdk_method = GDK_INTERP_NEAREST;
442           break;
443         case GST_PIXBUFSCALE_TILES:
444           src->gdk_method = GDK_INTERP_TILES;
445           break;
446         case GST_PIXBUFSCALE_BILINEAR:
447           src->gdk_method = GDK_INTERP_BILINEAR;
448           break;
449         case GST_PIXBUFSCALE_HYPER:
450           src->gdk_method = GDK_INTERP_HYPER;
451           break;
452       }
453       break;
454     default:
455       break;
456   }
457 }
458
459 static void
460 gst_pixbufscale_get_property (GObject * object, guint prop_id, GValue * value,
461     GParamSpec * pspec)
462 {
463   GstPixbufScale *src;
464
465   g_return_if_fail (GST_IS_PIXBUFSCALE (object));
466   src = GST_PIXBUFSCALE (object);
467
468   switch (prop_id) {
469     case ARG_METHOD:
470       g_value_set_enum (value, src->method);
471       break;
472     default:
473       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
474       break;
475   }
476 }
477
478
479 gboolean
480 pixbufscale_init (GstPlugin * plugin)
481 {
482   if (!gst_element_register (plugin, "gdkpixbufscale", GST_RANK_NONE,
483           GST_TYPE_PIXBUFSCALE))
484     return FALSE;
485
486   GST_DEBUG_CATEGORY_INIT (pixbufscale_debug, "gdkpixbufscale", 0,
487       "pixbufscale element");
488
489   return TRUE;
490 }