controller: port to new controller location and api
[platform/upstream/gstreamer.git] / gst / videofilter / gstgamma.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) 2003 Arwed v. Merkatz <v.merkatz@gmx.net>
5  * Copyright (C) 2006 Mark Nauwelaerts <manauw@skynet.be>
6  * Copyright (C) 2010 Sebastian Dröge <sebastian.droege@collabora.co.uk>
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Library General Public
10  * License as published by the Free Software Foundation; either
11  * version 2 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Library General Public License for more details.
17  *
18  * You should have received a copy of the GNU Library General Public
19  * License along with this library; if not, write to the
20  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
21  * Boston, MA 02111-1307, USA.
22  */
23
24 /*
25  * This file was (probably) generated from
26  * gstvideotemplate.c,v 1.12 2004/01/07 21:07:12 ds Exp 
27  * and
28  * make_filter,v 1.6 2004/01/07 21:33:01 ds Exp 
29  */
30
31 /**
32  * SECTION:element-gamma
33  *
34  * Performs gamma correction on a video stream.
35  *
36  * <refsect2>
37  * <title>Example launch line</title>
38  * |[
39  * gst-launch videotestsrc ! gamma gamma=2.0 ! ffmpegcolorspace ! ximagesink
40  * ]| This pipeline will make the image "brighter".
41  * |[
42  * gst-launch videotestsrc ! gamma gamma=0.5 ! ffmpegcolorspace ! ximagesink
43  * ]| This pipeline will make the image "darker".
44  * </refsect2>
45  *
46  * Last reviewed on 2010-04-18 (0.10.22)
47  */
48
49 #ifdef HAVE_CONFIG_H
50 #include "config.h"
51 #endif
52
53 #include "gstgamma.h"
54 #include <string.h>
55 #include <math.h>
56
57 #include <gst/video/video.h>
58
59 GST_DEBUG_CATEGORY_STATIC (gamma_debug);
60 #define GST_CAT_DEFAULT gamma_debug
61
62 /* GstGamma properties */
63 enum
64 {
65   PROP_0,
66   PROP_GAMMA
67       /* FILL ME */
68 };
69
70 #define DEFAULT_PROP_GAMMA  1
71
72 static GstStaticPadTemplate gst_gamma_src_template =
73 GST_STATIC_PAD_TEMPLATE ("src",
74     GST_PAD_SRC,
75     GST_PAD_ALWAYS,
76     GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE ("{ AYUV, "
77             "ARGB, BGRA, ABGR, RGBA, ABGR, RGBA, Y444, "
78             "xRGB, RGBx, xBGR, BGRx, RGB, BGR, Y42B, NV12, "
79             "NV21, YUY2, UYVY, YVYU, I420, YV12, IYUV, Y41B }"))
80     );
81
82 static GstStaticPadTemplate gst_gamma_sink_template =
83 GST_STATIC_PAD_TEMPLATE ("sink",
84     GST_PAD_SINK,
85     GST_PAD_ALWAYS,
86     GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE ("{ AYUV, "
87             "ARGB, BGRA, ABGR, RGBA, ABGR, RGBA, Y444, "
88             "xRGB, RGBx, xBGR, BGRx, RGB, BGR, Y42B, NV12, "
89             "NV21, YUY2, UYVY, YVYU, I420, YV12, IYUV, Y41B }"))
90     );
91
92 static void gst_gamma_set_property (GObject * object, guint prop_id,
93     const GValue * value, GParamSpec * pspec);
94 static void gst_gamma_get_property (GObject * object, guint prop_id,
95     GValue * value, GParamSpec * pspec);
96
97 static gboolean gst_gamma_set_caps (GstBaseTransform * base, GstCaps * incaps,
98     GstCaps * outcaps);
99 static GstFlowReturn gst_gamma_transform_ip (GstBaseTransform * transform,
100     GstBuffer * buf);
101 static void gst_gamma_before_transform (GstBaseTransform * transform,
102     GstBuffer * buf);
103
104 static void gst_gamma_calculate_tables (GstGamma * gamma);
105
106 G_DEFINE_TYPE (GstGamma, gst_gamma, GST_TYPE_VIDEO_FILTER);
107
108 static void
109 gst_gamma_class_init (GstGammaClass * g_class)
110 {
111   GObjectClass *gobject_class = (GObjectClass *) g_class;
112   GstElementClass *gstelement_class = (GstElementClass *) g_class;
113   GstBaseTransformClass *trans_class = (GstBaseTransformClass *) g_class;
114
115   GST_DEBUG_CATEGORY_INIT (gamma_debug, "gamma", 0, "gamma");
116
117   gobject_class->set_property = gst_gamma_set_property;
118   gobject_class->get_property = gst_gamma_get_property;
119
120   g_object_class_install_property (gobject_class, PROP_GAMMA,
121       g_param_spec_double ("gamma", "Gamma", "gamma",
122           0.01, 10, DEFAULT_PROP_GAMMA,
123           GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE));
124
125   gst_element_class_set_details_simple (gstelement_class,
126       "Video gamma correction", "Filter/Effect/Video",
127       "Adjusts gamma on a video stream", "Arwed v. Merkatz <v.merkatz@gmx.net");
128
129   gst_element_class_add_pad_template (gstelement_class,
130       gst_static_pad_template_get (&gst_gamma_sink_template));
131   gst_element_class_add_pad_template (gstelement_class,
132       gst_static_pad_template_get (&gst_gamma_src_template));
133
134   trans_class->set_caps = GST_DEBUG_FUNCPTR (gst_gamma_set_caps);
135   trans_class->transform_ip = GST_DEBUG_FUNCPTR (gst_gamma_transform_ip);
136   trans_class->before_transform =
137       GST_DEBUG_FUNCPTR (gst_gamma_before_transform);
138 }
139
140 static void
141 gst_gamma_init (GstGamma * gamma)
142 {
143   /* properties */
144   gamma->gamma = DEFAULT_PROP_GAMMA;
145   gst_gamma_calculate_tables (gamma);
146 }
147
148 static void
149 gst_gamma_set_property (GObject * object, guint prop_id, const GValue * value,
150     GParamSpec * pspec)
151 {
152   GstGamma *gamma = GST_GAMMA (object);
153
154   switch (prop_id) {
155     case PROP_GAMMA:{
156       gdouble val = g_value_get_double (value);
157
158       GST_DEBUG_OBJECT (gamma, "Changing gamma from %lf to %lf", gamma->gamma,
159           val);
160       GST_OBJECT_LOCK (gamma);
161       gamma->gamma = val;
162       gst_gamma_calculate_tables (gamma);
163       GST_OBJECT_UNLOCK (gamma);
164       break;
165     }
166     default:
167       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
168       break;
169   }
170 }
171
172 static void
173 gst_gamma_get_property (GObject * object, guint prop_id, GValue * value,
174     GParamSpec * pspec)
175 {
176   GstGamma *gamma = GST_GAMMA (object);
177
178   switch (prop_id) {
179     case PROP_GAMMA:
180       g_value_set_double (value, gamma->gamma);
181       break;
182     default:
183       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
184       break;
185   }
186 }
187
188 static void
189 gst_gamma_calculate_tables (GstGamma * gamma)
190 {
191   gint n;
192   gdouble val;
193   gdouble exp;
194
195   if (gamma->gamma == 1.0) {
196     GST_BASE_TRANSFORM (gamma)->passthrough = TRUE;
197     return;
198   }
199   GST_BASE_TRANSFORM (gamma)->passthrough = FALSE;
200
201   exp = 1.0 / gamma->gamma;
202   for (n = 0; n < 256; n++) {
203     val = n / 255.0;
204     val = pow (val, exp);
205     val = 255.0 * val;
206     gamma->gamma_table[n] = (guint8) floor (val + 0.5);
207   }
208 }
209
210 static void
211 gst_gamma_planar_yuv_ip (GstGamma * gamma, GstVideoFrame * frame)
212 {
213   gint i, j, height;
214   gint width, stride, row_wrap;
215   const guint8 *table = gamma->gamma_table;
216   guint8 *data;
217
218   data = GST_VIDEO_FRAME_COMP_DATA (frame, 0);
219   stride = GST_VIDEO_FRAME_COMP_STRIDE (frame, 0);
220   width = GST_VIDEO_FRAME_COMP_WIDTH (frame, 0);
221   height = GST_VIDEO_FRAME_COMP_HEIGHT (frame, 0);
222   row_wrap = stride - width;
223
224   for (i = 0; i < height; i++) {
225     for (j = 0; j < width; j++) {
226       *data = table[*data];
227       data++;
228     }
229     data += row_wrap;
230   }
231 }
232
233 static void
234 gst_gamma_packed_yuv_ip (GstGamma * gamma, GstVideoFrame * frame)
235 {
236   gint i, j, height;
237   gint width, stride, row_wrap;
238   gint pixel_stride;
239   const guint8 *table = gamma->gamma_table;
240   guint8 *data;
241
242   data = GST_VIDEO_FRAME_COMP_DATA (frame, 0);
243   stride = GST_VIDEO_FRAME_COMP_STRIDE (frame, 0);
244   width = GST_VIDEO_FRAME_COMP_WIDTH (frame, 0);
245   height = GST_VIDEO_FRAME_COMP_HEIGHT (frame, 0);
246   pixel_stride = GST_VIDEO_FRAME_COMP_PSTRIDE (frame, 0);
247   row_wrap = stride - pixel_stride * width;
248
249   for (i = 0; i < height; i++) {
250     for (j = 0; j < width; j++) {
251       *data = table[*data];
252       data += pixel_stride;
253     }
254     data += row_wrap;
255   }
256 }
257
258 static const int cog_ycbcr_to_rgb_matrix_8bit_sdtv[] = {
259   298, 0, 409, -57068,
260   298, -100, -208, 34707,
261   298, 516, 0, -70870,
262 };
263
264 static const gint cog_rgb_to_ycbcr_matrix_8bit_sdtv[] = {
265   66, 129, 25, 4096,
266   -38, -74, 112, 32768,
267   112, -94, -18, 32768,
268 };
269
270 #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)
271
272 static void
273 gst_gamma_packed_rgb_ip (GstGamma * gamma, GstVideoFrame * frame)
274 {
275   gint i, j, height;
276   gint width, stride, row_wrap;
277   gint pixel_stride;
278   const guint8 *table = gamma->gamma_table;
279   gint offsets[3];
280   gint r, g, b;
281   gint y, u, v;
282   guint8 *data;
283
284   data = GST_VIDEO_FRAME_PLANE_DATA (frame, 0);
285   stride = GST_VIDEO_FRAME_PLANE_STRIDE (frame, 0);
286   width = GST_VIDEO_FRAME_COMP_WIDTH (frame, 0);
287   height = GST_VIDEO_FRAME_COMP_HEIGHT (frame, 0);
288
289   offsets[0] = GST_VIDEO_FRAME_COMP_OFFSET (frame, 0);
290   offsets[1] = GST_VIDEO_FRAME_COMP_OFFSET (frame, 1);
291   offsets[2] = GST_VIDEO_FRAME_COMP_OFFSET (frame, 2);
292
293   pixel_stride = GST_VIDEO_FRAME_COMP_PSTRIDE (frame, 0);
294   row_wrap = stride - pixel_stride * width;
295
296   for (i = 0; i < height; i++) {
297     for (j = 0; j < width; j++) {
298       r = data[offsets[0]];
299       g = data[offsets[1]];
300       b = data[offsets[2]];
301
302       y = APPLY_MATRIX (cog_rgb_to_ycbcr_matrix_8bit_sdtv, 0, r, g, b);
303       u = APPLY_MATRIX (cog_rgb_to_ycbcr_matrix_8bit_sdtv, 1, r, g, b);
304       v = APPLY_MATRIX (cog_rgb_to_ycbcr_matrix_8bit_sdtv, 2, r, g, b);
305
306       y = table[CLAMP (y, 0, 255)];
307       r = APPLY_MATRIX (cog_ycbcr_to_rgb_matrix_8bit_sdtv, 0, y, u, v);
308       g = APPLY_MATRIX (cog_ycbcr_to_rgb_matrix_8bit_sdtv, 1, y, u, v);
309       b = APPLY_MATRIX (cog_ycbcr_to_rgb_matrix_8bit_sdtv, 2, y, u, v);
310
311       data[offsets[0]] = CLAMP (r, 0, 255);
312       data[offsets[1]] = CLAMP (g, 0, 255);
313       data[offsets[2]] = CLAMP (b, 0, 255);
314       data += pixel_stride;
315     }
316     data += row_wrap;
317   }
318 }
319
320 static gboolean
321 gst_gamma_set_caps (GstBaseTransform * base, GstCaps * incaps,
322     GstCaps * outcaps)
323 {
324   GstGamma *gamma = GST_GAMMA (base);
325   GstVideoInfo info;
326
327   GST_DEBUG_OBJECT (gamma,
328       "setting caps: in %" GST_PTR_FORMAT " out %" GST_PTR_FORMAT, incaps,
329       outcaps);
330
331   if (!gst_video_info_from_caps (&info, incaps))
332     goto invalid_caps;
333
334   gamma->info = info;
335
336   switch (GST_VIDEO_INFO_FORMAT (&info)) {
337     case GST_VIDEO_FORMAT_I420:
338     case GST_VIDEO_FORMAT_YV12:
339     case GST_VIDEO_FORMAT_Y41B:
340     case GST_VIDEO_FORMAT_Y42B:
341     case GST_VIDEO_FORMAT_Y444:
342     case GST_VIDEO_FORMAT_NV12:
343     case GST_VIDEO_FORMAT_NV21:
344       gamma->process = gst_gamma_planar_yuv_ip;
345       break;
346     case GST_VIDEO_FORMAT_YUY2:
347     case GST_VIDEO_FORMAT_UYVY:
348     case GST_VIDEO_FORMAT_AYUV:
349     case GST_VIDEO_FORMAT_YVYU:
350       gamma->process = gst_gamma_packed_yuv_ip;
351       break;
352     case GST_VIDEO_FORMAT_ARGB:
353     case GST_VIDEO_FORMAT_ABGR:
354     case GST_VIDEO_FORMAT_RGBA:
355     case GST_VIDEO_FORMAT_BGRA:
356     case GST_VIDEO_FORMAT_xRGB:
357     case GST_VIDEO_FORMAT_xBGR:
358     case GST_VIDEO_FORMAT_RGBx:
359     case GST_VIDEO_FORMAT_BGRx:
360     case GST_VIDEO_FORMAT_RGB:
361     case GST_VIDEO_FORMAT_BGR:
362       gamma->process = gst_gamma_packed_rgb_ip;
363       break;
364     default:
365       goto invalid_caps;
366       break;
367   }
368
369   return TRUE;
370
371 invalid_caps:
372   {
373     GST_ERROR_OBJECT (gamma, "Invalid caps: %" GST_PTR_FORMAT, incaps);
374     return FALSE;
375   }
376 }
377
378 static void
379 gst_gamma_before_transform (GstBaseTransform * base, GstBuffer * outbuf)
380 {
381   GstGamma *gamma = GST_GAMMA (base);
382   GstClockTime timestamp, stream_time;
383
384   timestamp = GST_BUFFER_TIMESTAMP (outbuf);
385   stream_time =
386       gst_segment_to_stream_time (&base->segment, GST_FORMAT_TIME, timestamp);
387
388   GST_DEBUG_OBJECT (gamma, "sync to %" GST_TIME_FORMAT,
389       GST_TIME_ARGS (timestamp));
390
391   if (GST_CLOCK_TIME_IS_VALID (stream_time))
392     gst_object_sync_values (GST_OBJECT (gamma), stream_time);
393 }
394
395 static GstFlowReturn
396 gst_gamma_transform_ip (GstBaseTransform * base, GstBuffer * outbuf)
397 {
398   GstGamma *gamma = GST_GAMMA (base);
399   GstVideoFrame frame;
400
401   if (!gamma->process)
402     goto not_negotiated;
403
404   if (base->passthrough)
405     goto done;
406
407   if (!gst_video_frame_map (&frame, &gamma->info, outbuf, GST_MAP_READWRITE))
408     goto wrong_buffer;
409
410   GST_OBJECT_LOCK (gamma);
411   gamma->process (gamma, &frame);
412   GST_OBJECT_UNLOCK (gamma);
413
414   gst_video_frame_unmap (&frame);
415
416 done:
417   return GST_FLOW_OK;
418
419   /* ERRORS */
420 wrong_buffer:
421   {
422     GST_ELEMENT_ERROR (gamma, STREAM, FORMAT,
423         (NULL), ("Invalid buffer received"));
424     return GST_FLOW_ERROR;
425   }
426 not_negotiated:
427   {
428     GST_ERROR_OBJECT (gamma, "Not negotiated yet");
429     return GST_FLOW_NOT_NEGOTIATED;
430   }
431 }