gst: Don't use GST_DEBUG_FUNCPTR for GObject vfuncs
[platform/upstream/gstreamer.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  * Copyright (C) <2010> Sebastian Dröge <sebastian.droege@collabora.co.uk>
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  * This file was (probably) generated from gstvideobalance.c,
24  * gstvideobalance.c,v 1.7 2003/11/08 02:48:59 dschleef Exp 
25  */
26
27 /**
28  * SECTION:element-videobalance
29  *
30  * Adjusts brightness, contrast, hue, saturation on a video stream.
31  *
32  * <refsect2>
33  * <title>Example launch line</title>
34  * |[
35  * gst-launch videotestsrc ! videobalance saturation=0.0 ! ffmpegcolorspace ! ximagesink
36  * ]| This pipeline converts the image to black and white by setting the
37  * saturation to 0.0.
38  * </refsect2>
39  *
40  * Last reviewed on 2010-04-18 (0.10.22)
41  */
42
43 #ifdef HAVE_CONFIG_H
44 #include "config.h"
45 #endif
46
47 #include "gstvideobalance.h"
48 #include <string.h>
49 #include <math.h>
50
51 #include <gst/controller/gstcontroller.h>
52 #include <gst/interfaces/colorbalance.h>
53
54 #ifndef M_PI
55 #define M_PI  3.14159265358979323846
56 #endif
57
58 #ifdef WIN32
59 #define rint(x) (floor((x)+0.5))
60 #endif
61
62 GST_DEBUG_CATEGORY_STATIC (videobalance_debug);
63 #define GST_CAT_DEFAULT videobalance_debug
64
65 /* GstVideoBalance properties */
66 #define DEFAULT_PROP_CONTRAST           1.0
67 #define DEFAULT_PROP_BRIGHTNESS         0.0
68 #define DEFAULT_PROP_HUE                0.0
69 #define DEFAULT_PROP_SATURATION         1.0
70
71 enum
72 {
73   PROP_0,
74   PROP_CONTRAST,
75   PROP_BRIGHTNESS,
76   PROP_HUE,
77   PROP_SATURATION
78 };
79
80 static GstStaticPadTemplate gst_video_balance_src_template =
81     GST_STATIC_PAD_TEMPLATE ("src",
82     GST_PAD_SRC,
83     GST_PAD_ALWAYS,
84     GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("AYUV") ";"
85         GST_VIDEO_CAPS_ARGB ";" GST_VIDEO_CAPS_BGRA ";"
86         GST_VIDEO_CAPS_ABGR ";" GST_VIDEO_CAPS_RGBA ";"
87         GST_VIDEO_CAPS_YUV ("Y444") ";"
88         GST_VIDEO_CAPS_xRGB ";" GST_VIDEO_CAPS_RGBx ";"
89         GST_VIDEO_CAPS_xBGR ";" GST_VIDEO_CAPS_BGRx ";"
90         GST_VIDEO_CAPS_RGB ";" GST_VIDEO_CAPS_BGR ";"
91         GST_VIDEO_CAPS_YUV ("Y42B") ";"
92         GST_VIDEO_CAPS_YUV ("YUY2") ";"
93         GST_VIDEO_CAPS_YUV ("UYVY") ";"
94         GST_VIDEO_CAPS_YUV ("YVYU") ";"
95         GST_VIDEO_CAPS_YUV ("I420") ";"
96         GST_VIDEO_CAPS_YUV ("YV12") ";"
97         GST_VIDEO_CAPS_YUV ("IYUV") ";" GST_VIDEO_CAPS_YUV ("Y41B")
98     )
99     );
100
101 static GstStaticPadTemplate gst_video_balance_sink_template =
102     GST_STATIC_PAD_TEMPLATE ("sink",
103     GST_PAD_SINK,
104     GST_PAD_ALWAYS,
105     GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("AYUV") ";"
106         GST_VIDEO_CAPS_ARGB ";" GST_VIDEO_CAPS_BGRA ";"
107         GST_VIDEO_CAPS_ABGR ";" GST_VIDEO_CAPS_RGBA ";"
108         GST_VIDEO_CAPS_YUV ("Y444") ";"
109         GST_VIDEO_CAPS_xRGB ";" GST_VIDEO_CAPS_RGBx ";"
110         GST_VIDEO_CAPS_xBGR ";" GST_VIDEO_CAPS_BGRx ";"
111         GST_VIDEO_CAPS_RGB ";" GST_VIDEO_CAPS_BGR ";"
112         GST_VIDEO_CAPS_YUV ("Y42B") ";"
113         GST_VIDEO_CAPS_YUV ("YUY2") ";"
114         GST_VIDEO_CAPS_YUV ("UYVY") ";"
115         GST_VIDEO_CAPS_YUV ("YVYU") ";"
116         GST_VIDEO_CAPS_YUV ("I420") ";"
117         GST_VIDEO_CAPS_YUV ("YV12") ";"
118         GST_VIDEO_CAPS_YUV ("IYUV") ";" GST_VIDEO_CAPS_YUV ("Y41B")
119     )
120     );
121
122 static void gst_video_balance_colorbalance_init (GstColorBalanceClass * iface);
123 static void gst_video_balance_interface_init (GstImplementsInterfaceClass *
124     klass);
125
126 static void gst_video_balance_set_property (GObject * object, guint prop_id,
127     const GValue * value, GParamSpec * pspec);
128 static void gst_video_balance_get_property (GObject * object, guint prop_id,
129     GValue * value, GParamSpec * pspec);
130
131 static void
132 _do_init (GType video_balance_type)
133 {
134   static const GInterfaceInfo iface_info = {
135     (GInterfaceInitFunc) gst_video_balance_interface_init,
136     NULL,
137     NULL,
138   };
139   static const GInterfaceInfo colorbalance_info = {
140     (GInterfaceInitFunc) gst_video_balance_colorbalance_init,
141     NULL,
142     NULL,
143   };
144
145   g_type_add_interface_static (video_balance_type,
146       GST_TYPE_IMPLEMENTS_INTERFACE, &iface_info);
147   g_type_add_interface_static (video_balance_type, GST_TYPE_COLOR_BALANCE,
148       &colorbalance_info);
149 }
150
151 GST_BOILERPLATE_FULL (GstVideoBalance, gst_video_balance, GstVideoFilter,
152     GST_TYPE_VIDEO_FILTER, _do_init);
153
154 /*
155  * look-up tables (LUT).
156  */
157 static void
158 gst_video_balance_update_tables (GstVideoBalance * vb)
159 {
160   gint i, j;
161   gdouble y, u, v, hue_cos, hue_sin;
162
163   /* Y */
164   for (i = 0; i < 256; i++) {
165     y = 16 + ((i - 16) * vb->contrast + vb->brightness * 255);
166     if (y < 0)
167       y = 0;
168     else if (y > 255)
169       y = 255;
170     vb->tabley[i] = rint (y);
171   }
172
173   hue_cos = cos (M_PI * vb->hue);
174   hue_sin = sin (M_PI * vb->hue);
175
176   /* U/V lookup tables are 2D, since we need both U/V for each table
177    * separately. */
178   for (i = -128; i < 128; i++) {
179     for (j = -128; j < 128; j++) {
180       u = 128 + ((i * hue_cos + j * hue_sin) * vb->saturation);
181       v = 128 + ((-i * hue_sin + j * hue_cos) * vb->saturation);
182       if (u < 0)
183         u = 0;
184       else if (u > 255)
185         u = 255;
186       if (v < 0)
187         v = 0;
188       else if (v > 255)
189         v = 255;
190       vb->tableu[i + 128][j + 128] = rint (u);
191       vb->tablev[i + 128][j + 128] = rint (v);
192     }
193   }
194 }
195
196 static gboolean
197 gst_video_balance_is_passthrough (GstVideoBalance * videobalance)
198 {
199   return videobalance->contrast == 1.0 &&
200       videobalance->brightness == 0.0 &&
201       videobalance->hue == 0.0 && videobalance->saturation == 1.0;
202 }
203
204 static void
205 gst_video_balance_update_properties (GstVideoBalance * videobalance)
206 {
207   gboolean passthrough = gst_video_balance_is_passthrough (videobalance);
208   GstBaseTransform *base = GST_BASE_TRANSFORM (videobalance);
209
210   GST_BASE_TRANSFORM_LOCK (base);
211   base->passthrough = passthrough;
212   GST_BASE_TRANSFORM_UNLOCK (base);
213
214   if (!passthrough)
215     gst_video_balance_update_tables (videobalance);
216 }
217
218 static void
219 gst_video_balance_planar_yuv (GstVideoBalance * videobalance, guint8 * data)
220 {
221   gint x, y;
222   guint8 *ydata;
223   guint8 *udata, *vdata;
224   gint ystride, ustride, vstride;
225   GstVideoFormat format;
226   gint width, height;
227   gint width2, height2;
228   guint8 *tabley = videobalance->tabley;
229   guint8 **tableu = videobalance->tableu;
230   guint8 **tablev = videobalance->tablev;
231
232   format = videobalance->format;
233   width = videobalance->width;
234   height = videobalance->height;
235
236   ydata =
237       data + gst_video_format_get_component_offset (format, 0, width, height);
238   ystride = gst_video_format_get_row_stride (format, 0, width);
239
240   for (y = 0; y < height; y++) {
241     guint8 *yptr;
242
243     yptr = ydata + y * ystride;
244     for (x = 0; x < width; x++) {
245       *ydata = tabley[*ydata];
246       ydata++;
247     }
248   }
249
250   width2 = gst_video_format_get_component_width (format, 1, width);
251   height2 = gst_video_format_get_component_height (format, 1, height);
252
253   udata =
254       data + gst_video_format_get_component_offset (format, 1, width, height);
255   vdata =
256       data + gst_video_format_get_component_offset (format, 2, width, height);
257   ustride = gst_video_format_get_row_stride (format, 1, width);
258   vstride = gst_video_format_get_row_stride (format, 1, width);
259
260   for (y = 0; y < height2; y++) {
261     guint8 *uptr, *vptr;
262     guint8 u1, v1;
263
264     uptr = udata + y * ustride;
265     vptr = vdata + y * vstride;
266
267     for (x = 0; x < width2; x++) {
268       u1 = *uptr;
269       v1 = *vptr;
270
271       *uptr++ = tableu[u1][v1];
272       *vptr++ = tablev[u1][v1];
273     }
274   }
275 }
276
277 static void
278 gst_video_balance_packed_yuv (GstVideoBalance * videobalance, guint8 * data)
279 {
280   gint x, y;
281   guint8 *ydata;
282   guint8 *udata, *vdata;
283   gint ystride, ustride, vstride;
284   gint yoff, uoff, voff;
285   GstVideoFormat format;
286   gint width, height;
287   gint width2, height2;
288   guint8 *tabley = videobalance->tabley;
289   guint8 **tableu = videobalance->tableu;
290   guint8 **tablev = videobalance->tablev;
291
292   format = videobalance->format;
293   width = videobalance->width;
294   height = videobalance->height;
295
296   ydata =
297       data + gst_video_format_get_component_offset (format, 0, width, height);
298   ystride = gst_video_format_get_row_stride (format, 0, width);
299   yoff = gst_video_format_get_pixel_stride (format, 0);
300
301   for (y = 0; y < height; y++) {
302     guint8 *yptr;
303
304     yptr = ydata + y * ystride;
305     for (x = 0; x < width; x++) {
306       *ydata = tabley[*ydata];
307       ydata += yoff;
308     }
309   }
310
311   width2 = gst_video_format_get_component_width (format, 1, width);
312   height2 = gst_video_format_get_component_height (format, 1, height);
313
314   udata =
315       data + gst_video_format_get_component_offset (format, 1, width, height);
316   vdata =
317       data + gst_video_format_get_component_offset (format, 2, width, height);
318   ustride = gst_video_format_get_row_stride (format, 1, width);
319   vstride = gst_video_format_get_row_stride (format, 1, width);
320   uoff = gst_video_format_get_pixel_stride (format, 1);
321   voff = gst_video_format_get_pixel_stride (format, 2);
322
323   for (y = 0; y < height2; y++) {
324     guint8 *uptr, *vptr;
325     guint8 u1, v1;
326
327     uptr = udata + y * ustride;
328     vptr = vdata + y * vstride;
329
330     for (x = 0; x < width2; x++) {
331       u1 = *uptr;
332       v1 = *vptr;
333
334       *uptr = tableu[u1][v1];
335       *vptr = tablev[u1][v1];
336
337       uptr += uoff;
338       vptr += voff;
339     }
340   }
341 }
342
343 static const int cog_ycbcr_to_rgb_matrix_8bit_sdtv[] = {
344   298, 0, 409, -57068,
345   298, -100, -208, 34707,
346   298, 516, 0, -70870,
347 };
348
349 static const gint cog_rgb_to_ycbcr_matrix_8bit_sdtv[] = {
350   66, 129, 25, 4096,
351   -38, -74, 112, 32768,
352   112, -94, -18, 32768,
353 };
354
355 #define APPLY_MATRIX(m,o,v1,v2,v3) ((m[o*4] * v1 + m[o*4+1] * v2 + m[o*4+2] * v3 + m[o*4+3]) >> 8)
356
357 static void
358 gst_video_balance_packed_rgb (GstVideoBalance * videobalance, guint8 * data)
359 {
360   gint i, j, height;
361   gint width, row_stride, row_wrap;
362   gint pixel_stride;
363   gint offsets[3];
364   gint r, g, b;
365   gint y, u, v;
366   gint u_tmp, v_tmp;
367   guint8 *tabley = videobalance->tabley;
368   guint8 **tableu = videobalance->tableu;
369   guint8 **tablev = videobalance->tablev;
370
371   offsets[0] = gst_video_format_get_component_offset (videobalance->format, 0,
372       videobalance->width, videobalance->height);
373   offsets[1] = gst_video_format_get_component_offset (videobalance->format, 1,
374       videobalance->width, videobalance->height);
375   offsets[2] = gst_video_format_get_component_offset (videobalance->format, 2,
376       videobalance->width, videobalance->height);
377
378   width =
379       gst_video_format_get_component_width (videobalance->format, 0,
380       videobalance->width);
381   height =
382       gst_video_format_get_component_height (videobalance->format, 0,
383       videobalance->height);
384   row_stride =
385       gst_video_format_get_row_stride (videobalance->format, 0,
386       videobalance->width);
387   pixel_stride = gst_video_format_get_pixel_stride (videobalance->format, 0);
388   row_wrap = row_stride - pixel_stride * width;
389
390   for (i = 0; i < height; i++) {
391     for (j = 0; j < width; j++) {
392       r = data[offsets[0]];
393       g = data[offsets[1]];
394       b = data[offsets[2]];
395
396       y = APPLY_MATRIX (cog_rgb_to_ycbcr_matrix_8bit_sdtv, 0, r, g, b);
397       u_tmp = APPLY_MATRIX (cog_rgb_to_ycbcr_matrix_8bit_sdtv, 1, r, g, b);
398       v_tmp = APPLY_MATRIX (cog_rgb_to_ycbcr_matrix_8bit_sdtv, 2, r, g, b);
399
400       y = CLAMP (y, 0, 255);
401       u_tmp = CLAMP (u_tmp, 0, 255);
402       v_tmp = CLAMP (v_tmp, 0, 255);
403
404       y = tabley[y];
405       u = tableu[u_tmp][v_tmp];
406       v = tablev[u_tmp][v_tmp];
407
408       r = APPLY_MATRIX (cog_ycbcr_to_rgb_matrix_8bit_sdtv, 0, y, u, v);
409       g = APPLY_MATRIX (cog_ycbcr_to_rgb_matrix_8bit_sdtv, 1, y, u, v);
410       b = APPLY_MATRIX (cog_ycbcr_to_rgb_matrix_8bit_sdtv, 2, y, u, v);
411
412       data[offsets[0]] = CLAMP (r, 0, 255);
413       data[offsets[1]] = CLAMP (g, 0, 255);
414       data[offsets[2]] = CLAMP (b, 0, 255);
415       data += pixel_stride;
416     }
417     data += row_wrap;
418   }
419 }
420
421 /* get notified of caps and plug in the correct process function */
422 static gboolean
423 gst_video_balance_set_caps (GstBaseTransform * base, GstCaps * incaps,
424     GstCaps * outcaps)
425 {
426   GstVideoBalance *videobalance = GST_VIDEO_BALANCE (base);
427
428   GST_DEBUG_OBJECT (videobalance,
429       "in %" GST_PTR_FORMAT " out %" GST_PTR_FORMAT, incaps, outcaps);
430
431   videobalance->process = NULL;
432
433   if (!gst_video_format_parse_caps (incaps, &videobalance->format,
434           &videobalance->width, &videobalance->height))
435     goto invalid_caps;
436
437   videobalance->size =
438       gst_video_format_get_size (videobalance->format, videobalance->width,
439       videobalance->height);
440
441   switch (videobalance->format) {
442     case GST_VIDEO_FORMAT_I420:
443     case GST_VIDEO_FORMAT_YV12:
444     case GST_VIDEO_FORMAT_Y41B:
445     case GST_VIDEO_FORMAT_Y42B:
446     case GST_VIDEO_FORMAT_Y444:
447       videobalance->process = gst_video_balance_planar_yuv;
448       break;
449     case GST_VIDEO_FORMAT_YUY2:
450     case GST_VIDEO_FORMAT_UYVY:
451     case GST_VIDEO_FORMAT_AYUV:
452     case GST_VIDEO_FORMAT_YVYU:
453       videobalance->process = gst_video_balance_packed_yuv;
454       break;
455     case GST_VIDEO_FORMAT_ARGB:
456     case GST_VIDEO_FORMAT_ABGR:
457     case GST_VIDEO_FORMAT_RGBA:
458     case GST_VIDEO_FORMAT_BGRA:
459     case GST_VIDEO_FORMAT_xRGB:
460     case GST_VIDEO_FORMAT_xBGR:
461     case GST_VIDEO_FORMAT_RGBx:
462     case GST_VIDEO_FORMAT_BGRx:
463     case GST_VIDEO_FORMAT_RGB:
464     case GST_VIDEO_FORMAT_BGR:
465       videobalance->process = gst_video_balance_packed_rgb;
466       break;
467     default:
468       break;
469   }
470
471   return videobalance->process != NULL;
472
473 invalid_caps:
474   GST_ERROR_OBJECT (videobalance, "Invalid caps: %" GST_PTR_FORMAT, incaps);
475   return FALSE;
476 }
477
478 static void
479 gst_video_balance_before_transform (GstBaseTransform * base, GstBuffer * buf)
480 {
481   GstVideoBalance *balance = GST_VIDEO_BALANCE (base);
482   GstClockTime timestamp, stream_time;
483
484   timestamp = GST_BUFFER_TIMESTAMP (buf);
485   stream_time =
486       gst_segment_to_stream_time (&base->segment, GST_FORMAT_TIME, timestamp);
487
488   GST_DEBUG_OBJECT (balance, "sync to %" GST_TIME_FORMAT,
489       GST_TIME_ARGS (timestamp));
490
491   if (GST_CLOCK_TIME_IS_VALID (stream_time))
492     gst_object_sync_values (G_OBJECT (balance), stream_time);
493 }
494
495 static GstFlowReturn
496 gst_video_balance_transform_ip (GstBaseTransform * base, GstBuffer * outbuf)
497 {
498   GstVideoBalance *videobalance = GST_VIDEO_BALANCE (base);
499   guint8 *data;
500   guint size;
501
502   if (!videobalance->process)
503     goto not_negotiated;
504
505   /* if no change is needed, we are done */
506   if (base->passthrough)
507     goto done;
508
509   data = GST_BUFFER_DATA (outbuf);
510   size = GST_BUFFER_SIZE (outbuf);
511
512   if (size != videobalance->size)
513     goto wrong_size;
514
515   GST_OBJECT_LOCK (videobalance);
516   videobalance->process (videobalance, data);
517   GST_OBJECT_UNLOCK (videobalance);
518
519 done:
520   return GST_FLOW_OK;
521
522   /* ERRORS */
523 wrong_size:
524   {
525     GST_ELEMENT_ERROR (videobalance, STREAM, FORMAT,
526         (NULL), ("Invalid buffer size %d, expected %d", size,
527             videobalance->size));
528     return GST_FLOW_ERROR;
529   }
530 not_negotiated:
531   GST_ERROR_OBJECT (videobalance, "Not negotiated yet");
532   return GST_FLOW_NOT_NEGOTIATED;
533 }
534
535 static void
536 gst_video_balance_base_init (gpointer g_class)
537 {
538   GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
539
540   gst_element_class_set_details_simple (element_class, "Video balance",
541       "Filter/Effect/Video",
542       "Adjusts brightness, contrast, hue, saturation on a video stream",
543       "David Schleef <ds@schleef.org>");
544
545   gst_element_class_add_pad_template (element_class,
546       gst_static_pad_template_get (&gst_video_balance_sink_template));
547   gst_element_class_add_pad_template (element_class,
548       gst_static_pad_template_get (&gst_video_balance_src_template));
549 }
550
551 static void
552 gst_video_balance_finalize (GObject * object)
553 {
554   GList *channels = NULL;
555   GstVideoBalance *balance = GST_VIDEO_BALANCE (object);
556
557   g_free (balance->tableu[0]);
558
559   channels = balance->channels;
560   while (channels) {
561     GstColorBalanceChannel *channel = channels->data;
562
563     g_object_unref (channel);
564     channels->data = NULL;
565     channels = g_list_next (channels);
566   }
567
568   if (balance->channels)
569     g_list_free (balance->channels);
570
571   G_OBJECT_CLASS (parent_class)->finalize (object);
572 }
573
574 static void
575 gst_video_balance_class_init (GstVideoBalanceClass * klass)
576 {
577   GObjectClass *gobject_class = (GObjectClass *) klass;
578   GstBaseTransformClass *trans_class = (GstBaseTransformClass *) klass;
579
580   GST_DEBUG_CATEGORY_INIT (videobalance_debug, "videobalance", 0,
581       "videobalance");
582
583   gobject_class->finalize = gst_video_balance_finalize;
584   gobject_class->set_property = gst_video_balance_set_property;
585   gobject_class->get_property = gst_video_balance_get_property;
586
587   g_object_class_install_property (gobject_class, PROP_CONTRAST,
588       g_param_spec_double ("contrast", "Contrast", "contrast",
589           0.0, 2.0, DEFAULT_PROP_CONTRAST,
590           GST_PARAM_CONTROLLABLE | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
591   g_object_class_install_property (gobject_class, PROP_BRIGHTNESS,
592       g_param_spec_double ("brightness", "Brightness", "brightness", -1.0, 1.0,
593           DEFAULT_PROP_BRIGHTNESS,
594           GST_PARAM_CONTROLLABLE | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
595   g_object_class_install_property (gobject_class, PROP_HUE,
596       g_param_spec_double ("hue", "Hue", "hue", -1.0, 1.0, DEFAULT_PROP_HUE,
597           GST_PARAM_CONTROLLABLE | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
598   g_object_class_install_property (gobject_class, PROP_SATURATION,
599       g_param_spec_double ("saturation", "Saturation", "saturation", 0.0, 2.0,
600           DEFAULT_PROP_SATURATION,
601           GST_PARAM_CONTROLLABLE | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
602
603   trans_class->set_caps = GST_DEBUG_FUNCPTR (gst_video_balance_set_caps);
604   trans_class->transform_ip =
605       GST_DEBUG_FUNCPTR (gst_video_balance_transform_ip);
606   trans_class->before_transform =
607       GST_DEBUG_FUNCPTR (gst_video_balance_before_transform);
608 }
609
610 static void
611 gst_video_balance_init (GstVideoBalance * videobalance,
612     GstVideoBalanceClass * klass)
613 {
614   const gchar *channels[4] = { "HUE", "SATURATION",
615     "BRIGHTNESS", "CONTRAST"
616   };
617   gint i;
618
619   /* Initialize propertiews */
620   videobalance->contrast = DEFAULT_PROP_CONTRAST;
621   videobalance->brightness = DEFAULT_PROP_BRIGHTNESS;
622   videobalance->hue = DEFAULT_PROP_HUE;
623   videobalance->saturation = DEFAULT_PROP_SATURATION;
624
625   videobalance->tableu[0] = g_new (guint8, 256 * 256 * 2);
626   for (i = 0; i < 256; i++) {
627     videobalance->tableu[i] =
628         videobalance->tableu[0] + i * 256 * sizeof (guint8);
629     videobalance->tablev[i] =
630         videobalance->tableu[0] + 256 * 256 * sizeof (guint8) +
631         i * 256 * sizeof (guint8);
632   }
633
634   gst_video_balance_update_properties (videobalance);
635
636   /* Generate the channels list */
637   for (i = 0; i < G_N_ELEMENTS (channels); i++) {
638     GstColorBalanceChannel *channel;
639
640     channel = g_object_new (GST_TYPE_COLOR_BALANCE_CHANNEL, NULL);
641     channel->label = g_strdup (channels[i]);
642     channel->min_value = -1000;
643     channel->max_value = 1000;
644
645     videobalance->channels = g_list_append (videobalance->channels, channel);
646   }
647 }
648
649 static gboolean
650 gst_video_balance_interface_supported (GstImplementsInterface * iface,
651     GType type)
652 {
653   g_assert (type == GST_TYPE_COLOR_BALANCE);
654   return TRUE;
655 }
656
657 static void
658 gst_video_balance_interface_init (GstImplementsInterfaceClass * klass)
659 {
660   klass->supported = gst_video_balance_interface_supported;
661 }
662
663 static const GList *
664 gst_video_balance_colorbalance_list_channels (GstColorBalance * balance)
665 {
666   GstVideoBalance *videobalance = GST_VIDEO_BALANCE (balance);
667
668   g_return_val_if_fail (videobalance != NULL, NULL);
669   g_return_val_if_fail (GST_IS_VIDEO_BALANCE (videobalance), NULL);
670
671   return videobalance->channels;
672 }
673
674 static void
675 gst_video_balance_colorbalance_set_value (GstColorBalance * balance,
676     GstColorBalanceChannel * channel, gint value)
677 {
678   GstVideoBalance *vb = GST_VIDEO_BALANCE (balance);
679   gdouble new_val;
680   gboolean changed;
681
682   g_return_if_fail (vb != NULL);
683   g_return_if_fail (GST_IS_VIDEO_BALANCE (vb));
684   g_return_if_fail (GST_IS_VIDEO_FILTER (vb));
685   g_return_if_fail (channel->label != NULL);
686
687   GST_OBJECT_LOCK (vb);
688   if (!g_ascii_strcasecmp (channel->label, "HUE")) {
689     new_val = (value + 1000.0) * 2.0 / 2000.0 - 1.0;
690     changed = new_val != vb->hue;
691     vb->hue = new_val;
692   } else if (!g_ascii_strcasecmp (channel->label, "SATURATION")) {
693     new_val = (value + 1000.0) * 2.0 / 2000.0;
694     changed = new_val != vb->saturation;
695     vb->saturation = new_val;
696   } else if (!g_ascii_strcasecmp (channel->label, "BRIGHTNESS")) {
697     new_val = (value + 1000.0) * 2.0 / 2000.0 - 1.0;
698     changed = new_val != vb->brightness;
699     vb->brightness = new_val;
700   } else if (!g_ascii_strcasecmp (channel->label, "CONTRAST")) {
701     new_val = (value + 1000.0) * 2.0 / 2000.0;
702     changed = new_val != vb->contrast;
703     vb->contrast = new_val;
704   }
705
706   gst_video_balance_update_properties (vb);
707   GST_OBJECT_UNLOCK (vb);
708
709   gst_color_balance_value_changed (balance, channel,
710       gst_color_balance_get_value (balance, channel));
711 }
712
713 static gint
714 gst_video_balance_colorbalance_get_value (GstColorBalance * balance,
715     GstColorBalanceChannel * channel)
716 {
717   GstVideoBalance *vb = GST_VIDEO_BALANCE (balance);
718   gint value = 0;
719
720   g_return_val_if_fail (vb != NULL, 0);
721   g_return_val_if_fail (GST_IS_VIDEO_BALANCE (vb), 0);
722   g_return_val_if_fail (channel->label != NULL, 0);
723
724   if (!g_ascii_strcasecmp (channel->label, "HUE")) {
725     value = (vb->hue + 1) * 2000.0 / 2.0 - 1000.0;
726   } else if (!g_ascii_strcasecmp (channel->label, "SATURATION")) {
727     value = vb->saturation * 2000.0 / 2.0 - 1000.0;
728   } else if (!g_ascii_strcasecmp (channel->label, "BRIGHTNESS")) {
729     value = (vb->brightness + 1) * 2000.0 / 2.0 - 1000.0;
730   } else if (!g_ascii_strcasecmp (channel->label, "CONTRAST")) {
731     value = vb->contrast * 2000.0 / 2.0 - 1000.0;
732   }
733
734   return value;
735 }
736
737 static void
738 gst_video_balance_colorbalance_init (GstColorBalanceClass * iface)
739 {
740   GST_COLOR_BALANCE_TYPE (iface) = GST_COLOR_BALANCE_SOFTWARE;
741   iface->list_channels = gst_video_balance_colorbalance_list_channels;
742   iface->set_value = gst_video_balance_colorbalance_set_value;
743   iface->get_value = gst_video_balance_colorbalance_get_value;
744 }
745
746 static GstColorBalanceChannel *
747 gst_video_balance_find_channel (GstVideoBalance * balance, const gchar * label)
748 {
749   GList *l;
750
751   for (l = balance->channels; l; l = l->next) {
752     GstColorBalanceChannel *channel = l->data;
753
754     if (g_ascii_strcasecmp (channel->label, label) == 0)
755       return channel;
756   }
757   return NULL;
758 }
759
760 static void
761 gst_video_balance_set_property (GObject * object, guint prop_id,
762     const GValue * value, GParamSpec * pspec)
763 {
764   GstVideoBalance *balance = GST_VIDEO_BALANCE (object);
765   gdouble d;
766   const gchar *label = NULL;
767
768   GST_OBJECT_LOCK (balance);
769   switch (prop_id) {
770     case PROP_CONTRAST:
771       d = g_value_get_double (value);
772       GST_DEBUG_OBJECT (balance, "Changing contrast from %lf to %lf",
773           balance->contrast, d);
774       if (d != balance->contrast)
775         label = "CONTRAST";
776       balance->contrast = d;
777       break;
778     case PROP_BRIGHTNESS:
779       d = g_value_get_double (value);
780       GST_DEBUG_OBJECT (balance, "Changing brightness from %lf to %lf",
781           balance->brightness, d);
782       if (d != balance->brightness)
783         label = "BRIGHTNESS";
784       balance->brightness = d;
785       break;
786     case PROP_HUE:
787       d = g_value_get_double (value);
788       GST_DEBUG_OBJECT (balance, "Changing hue from %lf to %lf", balance->hue,
789           d);
790       if (d != balance->hue)
791         label = "HUE";
792       balance->hue = d;
793       break;
794     case PROP_SATURATION:
795       d = g_value_get_double (value);
796       GST_DEBUG_OBJECT (balance, "Changing saturation from %lf to %lf",
797           balance->saturation, d);
798       if (d != balance->saturation)
799         label = "SATURATION";
800       balance->saturation = d;
801       break;
802     default:
803       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
804       break;
805   }
806
807   gst_video_balance_update_properties (balance);
808   GST_OBJECT_UNLOCK (balance);
809
810   if (label) {
811     GstColorBalanceChannel *channel =
812         gst_video_balance_find_channel (balance, label);
813     gst_color_balance_value_changed (GST_COLOR_BALANCE (balance), channel,
814         gst_color_balance_get_value (GST_COLOR_BALANCE (balance), channel));
815   }
816 }
817
818 static void
819 gst_video_balance_get_property (GObject * object, guint prop_id, GValue * value,
820     GParamSpec * pspec)
821 {
822   GstVideoBalance *balance = GST_VIDEO_BALANCE (object);
823
824   switch (prop_id) {
825     case PROP_CONTRAST:
826       g_value_set_double (value, balance->contrast);
827       break;
828     case PROP_BRIGHTNESS:
829       g_value_set_double (value, balance->brightness);
830       break;
831     case PROP_HUE:
832       g_value_set_double (value, balance->hue);
833       break;
834     case PROP_SATURATION:
835       g_value_set_double (value, balance->saturation);
836       break;
837     default:
838       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
839       break;
840   }
841 }