ddbf76b94a5539fb82f3376ebf2e8c1133628de3
[platform/upstream/gst-plugins-good.git] / gst / videofilter / gstvideobalance.c
1 /* GStreamer
2  * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
3  * Copyright (C) <2003> David Schleef <ds@schleef.org>
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., 59 Temple Place - Suite 330,
18  * Boston, MA 02111-1307, USA.
19  */
20
21 /*
22  * This file was (probably) generated from gstvideobalance.c,
23  * gstvideobalance.c,v 1.7 2003/11/08 02:48:59 dschleef Exp 
24  */
25
26 #ifdef HAVE_CONFIG_H
27 #include "config.h"
28 #endif
29
30 /*#define DEBUG_ENABLED */
31 #include <gstvideobalance.h>
32 #ifdef HAVE_LIBOIL
33 #include <liboil/liboil.h>
34 #endif
35 #include <string.h>
36 #include <math.h>
37
38 #include <gst/colorbalance/colorbalance.h>
39
40 /* GstVideobalance signals and args */
41 enum {
42   /* FILL ME */
43   LAST_SIGNAL
44 };
45
46 enum {
47   ARG_0,
48   ARG_CONTRAST,
49   ARG_BRIGHTNESS,
50   ARG_HUE,
51   ARG_SATURATION
52   /* FILL ME */
53 };
54
55 static GstVideofilterClass *parent_class = NULL;
56
57 static void     gst_videobalance_base_init      (gpointer g_class);
58 static void     gst_videobalance_class_init     (gpointer g_class, gpointer class_data);
59 static void     gst_videobalance_init           (GTypeInstance *instance, gpointer g_class);
60
61 static void     gst_videobalance_set_property           (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec);
62 static void     gst_videobalance_get_property           (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec);
63
64 static void gst_videobalance_planar411(GstVideofilter *videofilter, void *dest, void *src);
65 static void gst_videobalance_setup(GstVideofilter *videofilter);
66
67 static void gst_videobalance_interface_init (GstImplementsInterfaceClass *klass);
68 static void gst_videobalance_colorbalance_init (GstColorBalanceClass *iface);
69
70 static void gst_videobalance_dispose (GObject *object);
71 static void gst_videobalance_update_properties (GstVideobalance *videobalance);
72
73 GType
74 gst_videobalance_get_type (void)
75 {
76   static GType videobalance_type = 0;
77
78   if (!videobalance_type) {
79     static const GTypeInfo videobalance_info = {
80       sizeof(GstVideobalanceClass),
81       gst_videobalance_base_init,
82       NULL,
83       gst_videobalance_class_init,
84       NULL,
85       NULL,
86       sizeof(GstVideobalance),
87       0,
88       gst_videobalance_init,
89     };
90     
91     static const GInterfaceInfo iface_info = {
92       (GInterfaceInitFunc) gst_videobalance_interface_init,
93       NULL,
94       NULL,
95     };
96     
97     static const GInterfaceInfo colorbalance_info = {
98       (GInterfaceInitFunc) gst_videobalance_colorbalance_init,
99       NULL,
100       NULL,
101     };
102     
103     videobalance_type = g_type_register_static(GST_TYPE_VIDEOFILTER,
104         "GstVideobalance", &videobalance_info, 0);
105     
106     g_type_add_interface_static (videobalance_type, GST_TYPE_IMPLEMENTS_INTERFACE,
107                                  &iface_info);
108     g_type_add_interface_static (videobalance_type, GST_TYPE_COLOR_BALANCE,
109                                  &colorbalance_info);
110   }
111   return videobalance_type;
112 }
113
114 static GstVideofilterFormat gst_videobalance_formats[] = {
115   { "I420", 12, gst_videobalance_planar411, },
116 };
117
118   
119 static void
120 gst_videobalance_base_init (gpointer g_class)
121 {
122   static GstElementDetails videobalance_details = GST_ELEMENT_DETAILS (
123     "Video Balance Control",
124     "Filter/Effect/Video",
125     "Adjusts brightness, contrast, hue, saturation on a video stream",
126     "David Schleef <ds@schleef.org>"
127   );
128   GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
129   GstVideofilterClass *videofilter_class = GST_VIDEOFILTER_CLASS (g_class);
130   int i;
131   
132   gst_element_class_set_details (element_class, &videobalance_details);
133
134   for(i=0;i<G_N_ELEMENTS(gst_videobalance_formats);i++){
135     gst_videofilter_class_add_format(videofilter_class,
136         gst_videobalance_formats + i);
137   }
138
139   gst_videofilter_class_add_pad_templates (GST_VIDEOFILTER_CLASS (g_class));
140 }
141
142 static void
143 gst_videobalance_dispose (GObject *object)
144 {
145   GList *channels = NULL;
146   GstVideobalance *balance;
147   gint i;
148
149   balance = GST_VIDEOBALANCE (object);
150
151   for (i = 0; i < 256; i++) {
152     g_free (balance->tableu[i]);
153     g_free (balance->tablev[i]);
154   }
155   g_free (balance->tabley);
156   g_free (balance->tableu);
157   g_free (balance->tablev);
158   
159   channels = balance->channels;
160   
161   while (channels)
162     {
163       GstColorBalanceChannel *channel = channels->data;
164       g_object_unref (channel);
165       channels = g_list_next (channels);
166     }
167     
168   if (balance->channels)
169     g_list_free (balance->channels);
170   
171   G_OBJECT_CLASS (parent_class)->dispose (object);
172 }
173
174 static void
175 gst_videobalance_class_init (gpointer g_class, gpointer class_data)
176 {
177   GObjectClass *gobject_class;
178   GstVideofilterClass *videofilter_class;
179
180   gobject_class = G_OBJECT_CLASS (g_class);
181   videofilter_class = GST_VIDEOFILTER_CLASS (g_class);
182
183   parent_class = g_type_class_ref (GST_TYPE_VIDEOFILTER);
184   
185   g_object_class_install_property(gobject_class, ARG_CONTRAST,
186       g_param_spec_double("contrast","Contrast","contrast",
187       0, 2, 1, G_PARAM_READWRITE));
188   g_object_class_install_property(gobject_class, ARG_BRIGHTNESS,
189       g_param_spec_double("brightness","Brightness","brightness",
190       -1, 1, 0, G_PARAM_READWRITE));
191   g_object_class_install_property(gobject_class, ARG_HUE,
192       g_param_spec_double("hue","Hue","hue",
193       -1, 1, 0, G_PARAM_READWRITE));
194   g_object_class_install_property(gobject_class, ARG_SATURATION,
195       g_param_spec_double("saturation","Saturation","saturation",
196       0, 2, 1, G_PARAM_READWRITE));
197
198   gobject_class->set_property = gst_videobalance_set_property;
199   gobject_class->get_property = gst_videobalance_get_property;
200   gobject_class->dispose = gst_videobalance_dispose;
201
202   videofilter_class->setup = gst_videobalance_setup;
203
204 #ifdef HAVE_LIBOIL
205   oil_init();
206 #endif
207 }
208
209 static void
210 gst_videobalance_init (GTypeInstance *instance, gpointer g_class)
211 {
212   GstVideobalance *videobalance = GST_VIDEOBALANCE (instance);
213   GstVideofilter *videofilter;
214   char *channels[4] = { "HUE", "SATURATION",
215                         "BRIGHTNESS", "CONTRAST" };
216   gint i;
217                         
218   GST_DEBUG("gst_videobalance_init");
219
220   videofilter = GST_VIDEOFILTER(videobalance);
221
222   /* do stuff */
223   videobalance->contrast   = 1.0;
224   videobalance->brightness = 0.0;
225   videobalance->saturation = 1.0;
226   videobalance->hue        = 0.0;
227
228   videobalance->needupdate = FALSE;
229   videofilter->passthru = TRUE;
230
231   videobalance->tabley = g_new (guint8, 256);
232   videobalance->tableu = g_new (guint8 *, 256);
233   videobalance->tablev = g_new (guint8 *, 256);
234   for (i = 0; i < 256; i++) {
235     videobalance->tableu[i] = g_new (guint8, 256);
236     videobalance->tablev[i] = g_new (guint8, 256);
237   }
238                         
239   /* Generate the channels list */
240   for (i = 0; i < (sizeof (channels) / sizeof (char *)); i++)
241     {
242       GstColorBalanceChannel *channel;
243       
244       channel = g_object_new (GST_TYPE_COLOR_BALANCE_CHANNEL, NULL);
245       channel->label = g_strdup (channels[i]);
246       channel->min_value = -1000;
247       channel->max_value = 1000;
248       
249       videobalance->channels = g_list_append (videobalance->channels,
250                                               channel);
251     }
252
253 }
254
255 static gboolean
256 gst_videobalance_interface_supported (GstImplementsInterface *iface, GType type)
257 {
258   g_assert (type == GST_TYPE_COLOR_BALANCE);
259   return TRUE;
260 }
261
262 static void
263 gst_videobalance_interface_init (GstImplementsInterfaceClass *klass)
264 {
265   klass->supported = gst_videobalance_interface_supported;
266 }
267
268 static const GList *
269 gst_videobalance_colorbalance_list_channels (GstColorBalance *balance)
270 {
271   GstVideobalance *videobalance = GST_VIDEOBALANCE (balance);
272   
273   g_return_val_if_fail (videobalance != NULL, NULL);
274   g_return_val_if_fail (GST_IS_VIDEOBALANCE (videobalance), NULL);
275   
276   return videobalance->channels;
277 }
278
279 static void
280 gst_videobalance_colorbalance_set_value (GstColorBalance        *balance,
281                                          GstColorBalanceChannel *channel,
282                                          gint                    value)
283 {
284   GstVideobalance *vb = GST_VIDEOBALANCE (balance);
285   GstVideofilter *vf = GST_VIDEOFILTER (vb);
286   
287   g_return_if_fail (vb != NULL);
288   g_return_if_fail (GST_IS_VIDEOBALANCE (vb));
289   g_return_if_fail (GST_IS_VIDEOFILTER (vf));
290   g_return_if_fail (channel->label != NULL);
291   
292   if (!g_ascii_strcasecmp (channel->label, "HUE")) {
293     vb->hue = (value + 1000.0) * 2.0 / 2000.0 - 1.0;
294   } else if (!g_ascii_strcasecmp (channel->label, "SATURATION")) {
295     vb->saturation = (value + 1000.0) * 2.0 / 2000.0;
296   } else if (!g_ascii_strcasecmp (channel->label, "BRIGHTNESS")) {
297     vb->brightness = (value + 1000.0) * 2.0 / 2000.0 - 1.0;
298   } else if (!g_ascii_strcasecmp (channel->label, "CONTRAST")) {
299     vb->contrast = (value + 1000.0) * 2.0 / 2000.0;
300   }
301   
302   gst_videobalance_update_properties (vb);
303 }
304
305 static gint
306 gst_videobalance_colorbalance_get_value (GstColorBalance        *balance,
307                                          GstColorBalanceChannel *channel)
308 {
309   GstVideobalance *vb = GST_VIDEOBALANCE (balance);
310   gint value = 0;
311   
312   g_return_val_if_fail (vb != NULL, 0);
313   g_return_val_if_fail (GST_IS_VIDEOBALANCE (vb), 0);
314   g_return_val_if_fail (channel->label != NULL, 0);
315   
316   if (!g_ascii_strcasecmp (channel->label, "HUE")) {
317     value = (vb->hue + 1) * 2000.0 / 2.0 - 1000.0;
318   } else if (!g_ascii_strcasecmp (channel->label, "SATURATION")) {
319     value = vb->saturation * 2000.0 / 2.0 - 1000.0;
320   } else if (!g_ascii_strcasecmp (channel->label, "BRIGHTNESS")) {
321     value = (vb->brightness + 1) * 2000.0 / 2.0 - 1000.0;
322   } else if (!g_ascii_strcasecmp (channel->label, "CONTRAST")) {
323     value = vb->contrast * 2000.0 / 2.0 - 1000.0;
324   }
325   
326   return value;
327 }
328
329 static void
330 gst_videobalance_colorbalance_init (GstColorBalanceClass *iface)
331 {
332   GST_COLOR_BALANCE_TYPE (iface) = GST_COLOR_BALANCE_SOFTWARE;
333   iface->list_channels = gst_videobalance_colorbalance_list_channels;
334   iface->set_value = gst_videobalance_colorbalance_set_value;
335   iface->get_value = gst_videobalance_colorbalance_get_value;
336 }
337
338 static void
339 gst_videobalance_update_properties (GstVideobalance *videobalance)
340 {
341   GstVideofilter *vf = GST_VIDEOFILTER (videobalance);
342
343   videobalance->needupdate = TRUE;
344
345   if (videobalance->contrast == 1.0 &&
346       videobalance->brightness == 0.0 &&
347       videobalance->hue == 0.0 &&
348       videobalance->saturation == 1.0) {
349     vf->passthru = TRUE;
350   } else {
351     vf->passthru = FALSE;
352   }
353 }
354
355 static void
356 gst_videobalance_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
357 {
358   GstVideobalance *src;
359
360   /* it's not null if we got it, but it might not be ours */
361   g_return_if_fail(GST_IS_VIDEOBALANCE(object));
362   src = GST_VIDEOBALANCE(object);
363
364   GST_DEBUG("gst_videobalance_set_property");
365   switch (prop_id) {
366     case ARG_CONTRAST:
367       src->contrast = g_value_get_double (value);
368       break;
369     case ARG_BRIGHTNESS:
370       src->brightness = g_value_get_double (value);
371       break;
372     case ARG_HUE:
373       src->hue = g_value_get_double (value);
374       break;
375     case ARG_SATURATION:
376       src->saturation = g_value_get_double (value);
377       break;
378     default:
379       break;
380   }
381
382   gst_videobalance_update_properties (src);
383 }
384
385 static void
386 gst_videobalance_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
387 {
388   GstVideobalance *src;
389
390   /* it's not null if we got it, but it might not be ours */
391   g_return_if_fail(GST_IS_VIDEOBALANCE(object));
392   src = GST_VIDEOBALANCE(object);
393
394   switch (prop_id) {
395     case ARG_CONTRAST:
396       g_value_set_double (value, src->contrast);
397       break;
398     case ARG_BRIGHTNESS:
399       g_value_set_double (value, src->brightness);
400       break;
401     case ARG_HUE:
402       g_value_set_double (value, src->hue);
403       break;
404     case ARG_SATURATION:
405       g_value_set_double (value, src->saturation);
406       break;
407     default:
408       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
409       break;
410   }
411 }
412
413 static gboolean plugin_init (GstPlugin *plugin)
414 {
415   if(!gst_library_load("gstvideofilter"))
416     return FALSE;
417
418   return gst_element_register (plugin, "videobalance", GST_RANK_NONE,
419       GST_TYPE_VIDEOBALANCE);
420 }
421
422 GST_PLUGIN_DEFINE (
423   GST_VERSION_MAJOR,
424   GST_VERSION_MINOR,
425   "videobalance",
426   "Changes hue, saturation, brightness etc. on video images",
427   plugin_init,
428   VERSION,
429   GST_LICENSE,
430   GST_PACKAGE,
431   GST_ORIGIN
432 )
433
434 static void gst_videobalance_setup(GstVideofilter *videofilter)
435 {
436   GstVideobalance *videobalance;
437
438   g_return_if_fail(GST_IS_VIDEOBALANCE(videofilter));
439   videobalance = GST_VIDEOBALANCE(videofilter);
440
441   /* if any setup needs to be done, do it here */
442
443 }
444
445 /*
446  * look-up tables (LUT).
447  */
448
449 static void
450 gst_videobalance_update_tables_planar411 (GstVideobalance *vb)
451 {
452   gint i, j;
453   gdouble y, u, v, hue_cos, hue_sin;
454
455   /* Y */
456   for (i = 0; i < 256; i++) {
457     y = 16 + ((i - 16) * vb->contrast + vb->brightness * 255);
458     if (y < 0)
459       y = 0;
460     else if (y > 255)
461       y = 255;
462     vb->tabley[i] = rint (y);
463   }
464
465   /* FIXME this is a bogus transformation for hue, but you get
466    * the idea */
467   hue_cos = cos (M_PI * vb->hue);
468   hue_sin = sin (M_PI * vb->hue);
469
470   /* U/V lookup tables are 2D, since we need both U/V for each table
471    * separately. */
472   for (i = -128; i < 128; i++) {
473     for (j = -128; j < 128; j++) {
474       u = 128 + (( i * hue_cos + j * hue_sin) * vb->saturation);
475       v = 128 + ((-i * hue_sin + j * hue_cos) * vb->saturation);
476       if(u < 0)
477         u = 0;
478       else if (u > 255)
479         u = 255;
480       if (v < 0)
481         v = 0;
482       else if (v > 255)
483         v = 255;
484       vb->tableu[i+128][j+128] = rint (u);
485       vb->tablev[i+128][j+128] = rint (v);
486     }
487   }
488 }
489
490 #ifndef HAVE_LIBOIL
491 void tablelookup_u8 (guint8 *dest, int dstr, guint8 *src, int sstr,
492     guint8 *table, int tstr, int n)
493 {
494   int i;
495   for(i=0;i<n;i++){
496     *dest = table[*src * tstr];
497     dest += dstr;
498     src += sstr;
499   }
500 }
501 #endif
502
503 static void gst_videobalance_planar411(GstVideofilter *videofilter,
504     void *dest, void *src)
505 {
506   GstVideobalance *videobalance;
507   int width;
508   int height;
509   int x,y;
510
511   g_return_if_fail(GST_IS_VIDEOBALANCE(videofilter));
512   videobalance = GST_VIDEOBALANCE(videofilter);
513
514   if (videobalance->needupdate) {
515     gst_videobalance_update_tables_planar411 (videobalance);
516     videobalance->needupdate = FALSE;
517   }
518
519   width = videofilter->from_width;
520   height = videofilter->from_height;
521
522   {
523     guint8 *cdest = dest;
524     guint8 *csrc = src;
525
526     for(y=0;y<height;y++) {
527       tablelookup_u8 (cdest + y*width, 1, csrc + y*width, 1,
528           videobalance->tabley, 1, width);
529     }
530   }
531
532   {
533     gint u1, v1;
534     guint8 *usrc, *vsrc;
535     guint8 *udest, *vdest;
536
537     usrc = src + width*height;
538     udest = dest + width*height;
539     vsrc = src + width*height + (width/2)*(height/2);
540     vdest = dest + width*height + (width/2)*(height/2);
541
542     for(y=0;y<height/2;y++){
543       for(x=0;x<width/2;x++){
544         u1 = usrc[y*(width/2) + x];
545         v1 = vsrc[y*(width/2) + x];
546         udest[y*(width/2) + x] = videobalance->tableu[u1][v1];
547         vdest[y*(width/2) + x] = videobalance->tablev[u1][v1];
548       }
549     }
550   }
551
552 }