gst/videofilter/: Port gamma filter to 0.10. Fixes #412704.
[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  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Library General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Library General Public License for more details.
16  *
17  * You should have received a copy of the GNU Library General Public
18  * License along with this library; if not, write to the
19  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20  * Boston, MA 02111-1307, USA.
21  */
22
23 /*
24  * This file was (probably) generated from
25  * gstvideotemplate.c,v 1.12 2004/01/07 21:07:12 ds Exp 
26  * and
27  * make_filter,v 1.6 2004/01/07 21:33:01 ds Exp 
28  */
29
30 /**
31  * SECTION:element-gamma
32  *
33  * <refsect2>
34  * <para>
35  * Performs gamma correction on a video stream.
36  * </para>
37  * <title>Example launch line</title>
38  * <para>
39  * <programlisting>
40  * gst-launch videotestsrc ! gamma gamma=2.0 ! ffmpegcolorspace ! ximagesink
41  * </programlisting>
42  * This pipeline will make the image "brighter".
43  * </para>
44  * </refsect2>
45  */
46
47 #ifdef HAVE_CONFIG_H
48 #include "config.h"
49 #endif
50
51 #include "gstgamma.h"
52 #ifdef HAVE_LIBOIL
53 #include <liboil/liboil.h>
54 #endif
55 #include <string.h>
56 #include <math.h>
57
58 #include <gst/video/video.h>
59
60
61 GST_DEBUG_CATEGORY_STATIC (gamma_debug);
62 #define GST_CAT_DEFAULT gamma_debug
63
64 /* GstGamma signals and args */
65 enum
66 {
67   /* FILL ME */
68   LAST_SIGNAL
69 };
70
71 enum
72 {
73   PROP_0,
74   PROP_GAMMA
75       /* FILL ME */
76 };
77
78 #define DEFAULT_PROP_GAMMA  1
79
80 static const GstElementDetails gamma_details =
81 GST_ELEMENT_DETAILS ("Video gamma correction",
82     "Filter/Effect/Video",
83     "Adjusts gamma on a video stream",
84     "Arwed v. Merkatz <v.merkatz@gmx.net");
85
86 static GstStaticPadTemplate gst_gamma_src_template =
87 GST_STATIC_PAD_TEMPLATE ("src",
88     GST_PAD_SRC,
89     GST_PAD_ALWAYS,
90     GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("{ IYUV, I420, YV12 }"))
91     );
92
93 static GstStaticPadTemplate gst_gamma_sink_template =
94 GST_STATIC_PAD_TEMPLATE ("sink",
95     GST_PAD_SINK,
96     GST_PAD_ALWAYS,
97     GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("{ IYUV, I420, YV12 }"))
98     );
99
100 static void gst_gamma_set_property (GObject * object, guint prop_id,
101     const GValue * value, GParamSpec * pspec);
102 static void gst_gamma_get_property (GObject * object, guint prop_id,
103     GValue * value, GParamSpec * pspec);
104
105 static gboolean gst_gamma_set_caps (GstBaseTransform * base, GstCaps * incaps,
106     GstCaps * outcaps);
107 static GstFlowReturn gst_gamma_transform_ip (GstBaseTransform * transform,
108     GstBuffer * buf);
109
110 static void gst_gamma_planar411_ip (GstGamma * gamma, guint8 * data, gint size);
111 static void gst_gamma_calculate_tables (GstGamma * gamma);
112
113 GST_BOILERPLATE (GstGamma, gst_gamma, GstVideoFilter, GST_TYPE_VIDEO_FILTER);
114
115
116 static void
117 gst_gamma_base_init (gpointer g_class)
118 {
119   GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
120
121   gst_element_class_set_details (element_class, &gamma_details);
122
123   gst_element_class_add_pad_template (element_class,
124       gst_static_pad_template_get (&gst_gamma_sink_template));
125   gst_element_class_add_pad_template (element_class,
126       gst_static_pad_template_get (&gst_gamma_src_template));
127 }
128
129 static void
130 gst_gamma_class_init (GstGammaClass * g_class)
131 {
132   GObjectClass *gobject_class;
133   GstBaseTransformClass *trans_class;
134
135   gobject_class = G_OBJECT_CLASS (g_class);
136   trans_class = GST_BASE_TRANSFORM_CLASS (g_class);
137
138   gobject_class->set_property = gst_gamma_set_property;
139   gobject_class->get_property = gst_gamma_get_property;
140
141   g_object_class_install_property (gobject_class, PROP_GAMMA,
142       g_param_spec_double ("gamma", "Gamma", "gamma",
143           0.01, 10, DEFAULT_PROP_GAMMA, G_PARAM_READWRITE));
144
145   trans_class->set_caps = GST_DEBUG_FUNCPTR (gst_gamma_set_caps);
146   trans_class->transform_ip = GST_DEBUG_FUNCPTR (gst_gamma_transform_ip);
147 }
148
149 static void
150 gst_gamma_init (GstGamma * gamma, GstGammaClass * g_class)
151 {
152   GST_DEBUG_OBJECT (gamma, "gst_gamma_init");
153
154   /* properties */
155   gamma->gamma = DEFAULT_PROP_GAMMA;
156   gst_gamma_calculate_tables (gamma);
157 }
158
159 static void
160 gst_gamma_set_property (GObject * object, guint prop_id, const GValue * value,
161     GParamSpec * pspec)
162 {
163   GstGamma *gamma;
164
165   g_return_if_fail (GST_IS_GAMMA (object));
166   gamma = GST_GAMMA (object);
167
168   GST_DEBUG ("gst_gamma_set_property");
169   switch (prop_id) {
170     case PROP_GAMMA:
171       gamma->gamma = g_value_get_double (value);
172       gst_gamma_calculate_tables (gamma);
173       break;
174     default:
175       break;
176   }
177 }
178
179 static void
180 gst_gamma_get_property (GObject * object, guint prop_id, GValue * value,
181     GParamSpec * pspec)
182 {
183   GstGamma *gamma;
184
185   g_return_if_fail (GST_IS_GAMMA (object));
186   gamma = GST_GAMMA (object);
187
188   switch (prop_id) {
189     case PROP_GAMMA:
190       g_value_set_double (value, gamma->gamma);
191       break;
192     default:
193       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
194       break;
195   }
196 }
197
198 static void
199 gst_gamma_calculate_tables (GstGamma * gamma)
200 {
201   int n;
202   double val;
203   double exp;
204
205   if (gamma->gamma == 1.0) {
206     GST_BASE_TRANSFORM (gamma)->passthrough = TRUE;
207     return;
208   }
209   GST_BASE_TRANSFORM (gamma)->passthrough = FALSE;
210
211   exp = 1.0 / gamma->gamma;
212   for (n = 0; n < 256; n++) {
213     val = n / 255.0;
214     val = pow (val, exp);
215     val = 255.0 * val;
216     gamma->gamma_table[n] = (unsigned char) floor (val + 0.5);
217   }
218 }
219
220 #ifndef HAVE_LIBOIL
221 void
222 oil_tablelookup_u8 (guint8 * dest, int dstr, guint8 * src, int sstr,
223     guint8 * table, int tstr, int n)
224 {
225   int i;
226
227   for (i = 0; i < n; i++) {
228     *dest = table[*src * tstr];
229     dest += dstr;
230     src += sstr;
231   }
232 }
233 #endif
234
235 /* Useful macros */
236 #define GST_VIDEO_I420_Y_ROWSTRIDE(width) (GST_ROUND_UP_4(width))
237 #define GST_VIDEO_I420_U_ROWSTRIDE(width) (GST_ROUND_UP_8(width)/2)
238 #define GST_VIDEO_I420_V_ROWSTRIDE(width) ((GST_ROUND_UP_8(GST_VIDEO_I420_Y_ROWSTRIDE(width)))/2)
239
240 #define GST_VIDEO_I420_Y_OFFSET(w,h) (0)
241 #define GST_VIDEO_I420_U_OFFSET(w,h) (GST_VIDEO_I420_Y_OFFSET(w,h)+(GST_VIDEO_I420_Y_ROWSTRIDE(w)*GST_ROUND_UP_2(h)))
242 #define GST_VIDEO_I420_V_OFFSET(w,h) (GST_VIDEO_I420_U_OFFSET(w,h)+(GST_VIDEO_I420_U_ROWSTRIDE(w)*GST_ROUND_UP_2(h)/2))
243 #define GST_VIDEO_I420_SIZE(w,h)     (GST_VIDEO_I420_V_OFFSET(w,h)+(GST_VIDEO_I420_V_ROWSTRIDE(w)*GST_ROUND_UP_2(h)/2))
244
245 static gboolean
246 gst_gamma_set_caps (GstBaseTransform * base, GstCaps * incaps,
247     GstCaps * outcaps)
248 {
249   GstGamma *this;
250   GstStructure *structure;
251   gboolean res;
252
253   this = GST_GAMMA (base);
254
255   GST_DEBUG_OBJECT (this,
256       "set_caps: in %" GST_PTR_FORMAT " out %" GST_PTR_FORMAT, incaps, outcaps);
257
258   structure = gst_caps_get_structure (incaps, 0);
259
260   res = gst_structure_get_int (structure, "width", &this->width);
261   res &= gst_structure_get_int (structure, "height", &this->height);
262   if (!res)
263     goto done;
264
265   this->size = GST_VIDEO_I420_SIZE (this->width, this->height);
266
267 done:
268   return res;
269 }
270
271 static void
272 gst_gamma_planar411_ip (GstGamma * gamma, guint8 * data, gint size)
273 {
274   oil_tablelookup_u8 (data, 1, data, 1, gamma->gamma_table, 1, size);
275 }
276
277 static GstFlowReturn
278 gst_gamma_transform_ip (GstBaseTransform * base, GstBuffer * outbuf)
279 {
280   GstGamma *gamma;
281   guint8 *data;
282   guint size;
283
284   gamma = GST_GAMMA (base);
285
286   if (base->passthrough)
287     goto done;
288
289   data = GST_BUFFER_DATA (outbuf);
290   size = GST_BUFFER_SIZE (outbuf);
291
292   if (size != gamma->size)
293     goto wrong_size;
294
295   gst_gamma_planar411_ip (gamma, data,
296       gamma->height * GST_VIDEO_I420_Y_ROWSTRIDE (gamma->width));
297
298 done:
299   return GST_FLOW_OK;
300
301   /* ERRORS */
302 wrong_size:
303   {
304     GST_ELEMENT_ERROR (gamma, STREAM, FORMAT,
305         (NULL), ("Invalid buffer size %d, expected %d", size, gamma->size));
306     return GST_FLOW_ERROR;
307   }
308 }
309
310 static gboolean
311 plugin_init (GstPlugin * plugin)
312 {
313   GST_DEBUG_CATEGORY_INIT (gamma_debug, "gamma", 0, "gamma");
314
315   return gst_element_register (plugin, "gamma", GST_RANK_NONE, GST_TYPE_GAMMA);
316 }
317
318 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
319     GST_VERSION_MINOR,
320     "gamma",
321     "Changes gamma on video images",
322     plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN);