tizen 2.0 init
[framework/multimedia/gst-plugins-good0.10.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 #include <gst/controller/gstcontroller.h>
59
60 GST_DEBUG_CATEGORY_STATIC (gamma_debug);
61 #define GST_CAT_DEFAULT gamma_debug
62
63 /* GstGamma properties */
64 enum
65 {
66   PROP_0,
67   PROP_GAMMA
68       /* FILL ME */
69 };
70
71 #define DEFAULT_PROP_GAMMA  1
72
73 static GstStaticPadTemplate gst_gamma_src_template =
74     GST_STATIC_PAD_TEMPLATE ("src",
75     GST_PAD_SRC,
76     GST_PAD_ALWAYS,
77     GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("AYUV") ";"
78         GST_VIDEO_CAPS_ARGB ";" GST_VIDEO_CAPS_BGRA ";"
79         GST_VIDEO_CAPS_ABGR ";" GST_VIDEO_CAPS_RGBA ";"
80         GST_VIDEO_CAPS_YUV ("Y444") ";"
81         GST_VIDEO_CAPS_xRGB ";" GST_VIDEO_CAPS_RGBx ";"
82         GST_VIDEO_CAPS_xBGR ";" GST_VIDEO_CAPS_BGRx ";"
83         GST_VIDEO_CAPS_RGB ";" GST_VIDEO_CAPS_BGR ";"
84         GST_VIDEO_CAPS_YUV ("Y42B") ";"
85         GST_VIDEO_CAPS_YUV ("NV12") ";"
86         GST_VIDEO_CAPS_YUV ("NV21") ";"
87         GST_VIDEO_CAPS_YUV ("YUY2") ";"
88         GST_VIDEO_CAPS_YUV ("UYVY") ";"
89         GST_VIDEO_CAPS_YUV ("YVYU") ";"
90         GST_VIDEO_CAPS_YUV ("I420") ";"
91         GST_VIDEO_CAPS_YUV ("YV12") ";"
92         GST_VIDEO_CAPS_YUV ("IYUV") ";" GST_VIDEO_CAPS_YUV ("Y41B")
93     )
94     );
95
96 static GstStaticPadTemplate gst_gamma_sink_template =
97     GST_STATIC_PAD_TEMPLATE ("sink",
98     GST_PAD_SINK,
99     GST_PAD_ALWAYS,
100     GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("AYUV") ";"
101         GST_VIDEO_CAPS_ARGB ";" GST_VIDEO_CAPS_BGRA ";"
102         GST_VIDEO_CAPS_ABGR ";" GST_VIDEO_CAPS_RGBA ";"
103         GST_VIDEO_CAPS_YUV ("Y444") ";"
104         GST_VIDEO_CAPS_xRGB ";" GST_VIDEO_CAPS_RGBx ";"
105         GST_VIDEO_CAPS_xBGR ";" GST_VIDEO_CAPS_BGRx ";"
106         GST_VIDEO_CAPS_RGB ";" GST_VIDEO_CAPS_BGR ";"
107         GST_VIDEO_CAPS_YUV ("Y42B") ";"
108         GST_VIDEO_CAPS_YUV ("NV12") ";"
109         GST_VIDEO_CAPS_YUV ("NV21") ";"
110         GST_VIDEO_CAPS_YUV ("YUY2") ";"
111         GST_VIDEO_CAPS_YUV ("UYVY") ";"
112         GST_VIDEO_CAPS_YUV ("YVYU") ";"
113         GST_VIDEO_CAPS_YUV ("I420") ";"
114         GST_VIDEO_CAPS_YUV ("YV12") ";"
115         GST_VIDEO_CAPS_YUV ("IYUV") ";" GST_VIDEO_CAPS_YUV ("Y41B")
116     )
117     );
118
119 static void gst_gamma_set_property (GObject * object, guint prop_id,
120     const GValue * value, GParamSpec * pspec);
121 static void gst_gamma_get_property (GObject * object, guint prop_id,
122     GValue * value, GParamSpec * pspec);
123
124 static gboolean gst_gamma_set_caps (GstBaseTransform * base, GstCaps * incaps,
125     GstCaps * outcaps);
126 static GstFlowReturn gst_gamma_transform_ip (GstBaseTransform * transform,
127     GstBuffer * buf);
128 static void gst_gamma_before_transform (GstBaseTransform * transform,
129     GstBuffer * buf);
130
131 static void gst_gamma_calculate_tables (GstGamma * gamma);
132
133 GST_BOILERPLATE (GstGamma, gst_gamma, GstVideoFilter, GST_TYPE_VIDEO_FILTER);
134
135 static void
136 gst_gamma_base_init (gpointer g_class)
137 {
138   GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
139
140   gst_element_class_set_details_simple (element_class, "Video gamma correction",
141       "Filter/Effect/Video",
142       "Adjusts gamma on a video stream",
143       "Arwed v. Merkatz <v.merkatz@gmx.net>");
144
145   gst_element_class_add_static_pad_template (element_class,
146       &gst_gamma_sink_template);
147   gst_element_class_add_static_pad_template (element_class,
148       &gst_gamma_src_template);
149 }
150
151 static void
152 gst_gamma_class_init (GstGammaClass * g_class)
153 {
154   GObjectClass *gobject_class = (GObjectClass *) g_class;
155   GstBaseTransformClass *trans_class = (GstBaseTransformClass *) g_class;
156
157   GST_DEBUG_CATEGORY_INIT (gamma_debug, "gamma", 0, "gamma");
158
159   gobject_class->set_property = gst_gamma_set_property;
160   gobject_class->get_property = gst_gamma_get_property;
161
162   g_object_class_install_property (gobject_class, PROP_GAMMA,
163       g_param_spec_double ("gamma", "Gamma", "gamma",
164           0.01, 10, DEFAULT_PROP_GAMMA,
165           GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE));
166
167   trans_class->set_caps = GST_DEBUG_FUNCPTR (gst_gamma_set_caps);
168   trans_class->transform_ip = GST_DEBUG_FUNCPTR (gst_gamma_transform_ip);
169   trans_class->before_transform =
170       GST_DEBUG_FUNCPTR (gst_gamma_before_transform);
171 }
172
173 static void
174 gst_gamma_init (GstGamma * gamma, GstGammaClass * g_class)
175 {
176   /* properties */
177   gamma->gamma = DEFAULT_PROP_GAMMA;
178   gst_gamma_calculate_tables (gamma);
179 }
180
181 static void
182 gst_gamma_set_property (GObject * object, guint prop_id, const GValue * value,
183     GParamSpec * pspec)
184 {
185   GstGamma *gamma = GST_GAMMA (object);
186
187   switch (prop_id) {
188     case PROP_GAMMA:{
189       gdouble val = g_value_get_double (value);
190
191       GST_DEBUG_OBJECT (gamma, "Changing gamma from %lf to %lf", gamma->gamma,
192           val);
193       GST_OBJECT_LOCK (gamma);
194       gamma->gamma = val;
195       gst_gamma_calculate_tables (gamma);
196       GST_OBJECT_UNLOCK (gamma);
197       break;
198     }
199     default:
200       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
201       break;
202   }
203 }
204
205 static void
206 gst_gamma_get_property (GObject * object, guint prop_id, GValue * value,
207     GParamSpec * pspec)
208 {
209   GstGamma *gamma = GST_GAMMA (object);
210
211   switch (prop_id) {
212     case PROP_GAMMA:
213       g_value_set_double (value, gamma->gamma);
214       break;
215     default:
216       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
217       break;
218   }
219 }
220
221 static void
222 gst_gamma_calculate_tables (GstGamma * gamma)
223 {
224   gint n;
225   gdouble val;
226   gdouble exp;
227
228   if (gamma->gamma == 1.0) {
229     GST_BASE_TRANSFORM (gamma)->passthrough = TRUE;
230     return;
231   }
232   GST_BASE_TRANSFORM (gamma)->passthrough = FALSE;
233
234   exp = 1.0 / gamma->gamma;
235   for (n = 0; n < 256; n++) {
236     val = n / 255.0;
237     val = pow (val, exp);
238     val = 255.0 * val;
239     gamma->gamma_table[n] = (guint8) floor (val + 0.5);
240   }
241 }
242
243 static void
244 gst_gamma_planar_yuv_ip (GstGamma * gamma, guint8 * data)
245 {
246   gint i, j, height;
247   gint width, row_stride, row_wrap;
248   const guint8 *table = gamma->gamma_table;
249
250   data =
251       data + gst_video_format_get_component_offset (gamma->format, 0,
252       gamma->width, gamma->height);
253
254   width = gst_video_format_get_component_width (gamma->format, 0, gamma->width);
255   height = gst_video_format_get_component_height (gamma->format, 0,
256       gamma->height);
257   row_stride = gst_video_format_get_row_stride (gamma->format, 0, gamma->width);
258   row_wrap = row_stride - width;
259
260   for (i = 0; i < height; i++) {
261     for (j = 0; j < width; j++) {
262       *data = table[*data];
263       data++;
264     }
265     data += row_wrap;
266   }
267 }
268
269 static void
270 gst_gamma_packed_yuv_ip (GstGamma * gamma, guint8 * data)
271 {
272   gint i, j, height;
273   gint width, row_stride, row_wrap;
274   gint pixel_stride;
275   const guint8 *table = gamma->gamma_table;
276
277   data = data + gst_video_format_get_component_offset (gamma->format, 0,
278       gamma->width, gamma->height);
279
280   width = gst_video_format_get_component_width (gamma->format, 0, gamma->width);
281   height = gst_video_format_get_component_height (gamma->format, 0,
282       gamma->height);
283   row_stride = gst_video_format_get_row_stride (gamma->format, 0, gamma->width);
284   pixel_stride = gst_video_format_get_pixel_stride (gamma->format, 0);
285   row_wrap = row_stride - pixel_stride * width;
286
287   for (i = 0; i < height; i++) {
288     for (j = 0; j < width; j++) {
289       *data = table[*data];
290       data += pixel_stride;
291     }
292     data += row_wrap;
293   }
294 }
295
296 static const int cog_ycbcr_to_rgb_matrix_8bit_sdtv[] = {
297   298, 0, 409, -57068,
298   298, -100, -208, 34707,
299   298, 516, 0, -70870,
300 };
301
302 static const gint cog_rgb_to_ycbcr_matrix_8bit_sdtv[] = {
303   66, 129, 25, 4096,
304   -38, -74, 112, 32768,
305   112, -94, -18, 32768,
306 };
307
308 #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)
309
310 static void
311 gst_gamma_packed_rgb_ip (GstGamma * gamma, guint8 * data)
312 {
313   gint i, j, height;
314   gint width, row_stride, row_wrap;
315   gint pixel_stride;
316   const guint8 *table = gamma->gamma_table;
317   gint offsets[3];
318   gint r, g, b;
319   gint y, u, v;
320
321   offsets[0] = gst_video_format_get_component_offset (gamma->format, 0,
322       gamma->width, gamma->height);
323   offsets[1] = gst_video_format_get_component_offset (gamma->format, 1,
324       gamma->width, gamma->height);
325   offsets[2] = gst_video_format_get_component_offset (gamma->format, 2,
326       gamma->width, gamma->height);
327
328   width = gst_video_format_get_component_width (gamma->format, 0, gamma->width);
329   height = gst_video_format_get_component_height (gamma->format, 0,
330       gamma->height);
331   row_stride = gst_video_format_get_row_stride (gamma->format, 0, gamma->width);
332   pixel_stride = gst_video_format_get_pixel_stride (gamma->format, 0);
333   row_wrap = row_stride - pixel_stride * width;
334
335   for (i = 0; i < height; i++) {
336     for (j = 0; j < width; j++) {
337       r = data[offsets[0]];
338       g = data[offsets[1]];
339       b = data[offsets[2]];
340
341       y = APPLY_MATRIX (cog_rgb_to_ycbcr_matrix_8bit_sdtv, 0, r, g, b);
342       u = APPLY_MATRIX (cog_rgb_to_ycbcr_matrix_8bit_sdtv, 1, r, g, b);
343       v = APPLY_MATRIX (cog_rgb_to_ycbcr_matrix_8bit_sdtv, 2, r, g, b);
344
345       y = table[CLAMP (y, 0, 255)];
346       r = APPLY_MATRIX (cog_ycbcr_to_rgb_matrix_8bit_sdtv, 0, y, u, v);
347       g = APPLY_MATRIX (cog_ycbcr_to_rgb_matrix_8bit_sdtv, 1, y, u, v);
348       b = APPLY_MATRIX (cog_ycbcr_to_rgb_matrix_8bit_sdtv, 2, y, u, v);
349
350       data[offsets[0]] = CLAMP (r, 0, 255);
351       data[offsets[1]] = CLAMP (g, 0, 255);
352       data[offsets[2]] = CLAMP (b, 0, 255);
353       data += pixel_stride;
354     }
355     data += row_wrap;
356   }
357 }
358
359 static gboolean
360 gst_gamma_set_caps (GstBaseTransform * base, GstCaps * incaps,
361     GstCaps * outcaps)
362 {
363   GstGamma *gamma = GST_GAMMA (base);
364
365   GST_DEBUG_OBJECT (gamma,
366       "setting caps: in %" GST_PTR_FORMAT " out %" GST_PTR_FORMAT, incaps,
367       outcaps);
368
369   if (!gst_video_format_parse_caps (incaps, &gamma->format, &gamma->width,
370           &gamma->height))
371     goto invalid_caps;
372
373   gamma->size =
374       gst_video_format_get_size (gamma->format, gamma->width, gamma->height);
375
376   switch (gamma->format) {
377     case GST_VIDEO_FORMAT_I420:
378     case GST_VIDEO_FORMAT_YV12:
379     case GST_VIDEO_FORMAT_Y41B:
380     case GST_VIDEO_FORMAT_Y42B:
381     case GST_VIDEO_FORMAT_Y444:
382     case GST_VIDEO_FORMAT_NV12:
383     case GST_VIDEO_FORMAT_NV21:
384       gamma->process = gst_gamma_planar_yuv_ip;
385       break;
386     case GST_VIDEO_FORMAT_YUY2:
387     case GST_VIDEO_FORMAT_UYVY:
388     case GST_VIDEO_FORMAT_AYUV:
389     case GST_VIDEO_FORMAT_YVYU:
390       gamma->process = gst_gamma_packed_yuv_ip;
391       break;
392     case GST_VIDEO_FORMAT_ARGB:
393     case GST_VIDEO_FORMAT_ABGR:
394     case GST_VIDEO_FORMAT_RGBA:
395     case GST_VIDEO_FORMAT_BGRA:
396     case GST_VIDEO_FORMAT_xRGB:
397     case GST_VIDEO_FORMAT_xBGR:
398     case GST_VIDEO_FORMAT_RGBx:
399     case GST_VIDEO_FORMAT_BGRx:
400     case GST_VIDEO_FORMAT_RGB:
401     case GST_VIDEO_FORMAT_BGR:
402       gamma->process = gst_gamma_packed_rgb_ip;
403       break;
404     default:
405       goto invalid_caps;
406       break;
407   }
408
409   return TRUE;
410
411 invalid_caps:
412   GST_ERROR_OBJECT (gamma, "Invalid caps: %" GST_PTR_FORMAT, incaps);
413   return FALSE;
414 }
415
416 static void
417 gst_gamma_before_transform (GstBaseTransform * base, GstBuffer * outbuf)
418 {
419   GstGamma *gamma = GST_GAMMA (base);
420   GstClockTime timestamp, stream_time;
421
422   timestamp = GST_BUFFER_TIMESTAMP (outbuf);
423   stream_time =
424       gst_segment_to_stream_time (&base->segment, GST_FORMAT_TIME, timestamp);
425
426   GST_DEBUG_OBJECT (gamma, "sync to %" GST_TIME_FORMAT,
427       GST_TIME_ARGS (timestamp));
428
429   if (GST_CLOCK_TIME_IS_VALID (stream_time))
430     gst_object_sync_values (G_OBJECT (gamma), stream_time);
431 }
432
433 static GstFlowReturn
434 gst_gamma_transform_ip (GstBaseTransform * base, GstBuffer * outbuf)
435 {
436   GstGamma *gamma = GST_GAMMA (base);
437   guint8 *data;
438   guint size;
439
440   if (!gamma->process)
441     goto not_negotiated;
442
443   if (base->passthrough)
444     goto done;
445
446   data = GST_BUFFER_DATA (outbuf);
447   size = GST_BUFFER_SIZE (outbuf);
448
449   if (size != gamma->size)
450     goto wrong_size;
451
452   GST_OBJECT_LOCK (gamma);
453   gamma->process (gamma, data);
454   GST_OBJECT_UNLOCK (gamma);
455
456 done:
457   return GST_FLOW_OK;
458
459   /* ERRORS */
460 wrong_size:
461   {
462     GST_ELEMENT_ERROR (gamma, STREAM, FORMAT,
463         (NULL), ("Invalid buffer size %d, expected %d", size, gamma->size));
464     return GST_FLOW_ERROR;
465   }
466 not_negotiated:
467   {
468     GST_ERROR_OBJECT (gamma, "Not negotiated yet");
469     return GST_FLOW_NOT_NEGOTIATED;
470   }
471 }