upload tizen1.0 source
[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", "Arwed v. Merkatz <v.merkatz@gmx.net");
143
144   gst_element_class_add_pad_template (element_class,
145       gst_static_pad_template_get (&gst_gamma_sink_template));
146   gst_element_class_add_pad_template (element_class,
147       gst_static_pad_template_get (&gst_gamma_src_template));
148 }
149
150 static void
151 gst_gamma_class_init (GstGammaClass * g_class)
152 {
153   GObjectClass *gobject_class = (GObjectClass *) g_class;
154   GstBaseTransformClass *trans_class = (GstBaseTransformClass *) g_class;
155
156   GST_DEBUG_CATEGORY_INIT (gamma_debug, "gamma", 0, "gamma");
157
158   gobject_class->set_property = gst_gamma_set_property;
159   gobject_class->get_property = gst_gamma_get_property;
160
161   g_object_class_install_property (gobject_class, PROP_GAMMA,
162       g_param_spec_double ("gamma", "Gamma", "gamma",
163           0.01, 10, DEFAULT_PROP_GAMMA,
164           GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE));
165
166   trans_class->set_caps = GST_DEBUG_FUNCPTR (gst_gamma_set_caps);
167   trans_class->transform_ip = GST_DEBUG_FUNCPTR (gst_gamma_transform_ip);
168   trans_class->before_transform =
169       GST_DEBUG_FUNCPTR (gst_gamma_before_transform);
170 }
171
172 static void
173 gst_gamma_init (GstGamma * gamma, GstGammaClass * g_class)
174 {
175   /* properties */
176   gamma->gamma = DEFAULT_PROP_GAMMA;
177   gst_gamma_calculate_tables (gamma);
178 }
179
180 static void
181 gst_gamma_set_property (GObject * object, guint prop_id, const GValue * value,
182     GParamSpec * pspec)
183 {
184   GstGamma *gamma = GST_GAMMA (object);
185
186   switch (prop_id) {
187     case PROP_GAMMA:{
188       gdouble val = g_value_get_double (value);
189
190       GST_DEBUG_OBJECT (gamma, "Changing gamma from %lf to %lf", gamma->gamma,
191           val);
192       GST_OBJECT_LOCK (gamma);
193       gamma->gamma = val;
194       gst_gamma_calculate_tables (gamma);
195       GST_OBJECT_UNLOCK (gamma);
196       break;
197     }
198     default:
199       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
200       break;
201   }
202 }
203
204 static void
205 gst_gamma_get_property (GObject * object, guint prop_id, GValue * value,
206     GParamSpec * pspec)
207 {
208   GstGamma *gamma = GST_GAMMA (object);
209
210   switch (prop_id) {
211     case PROP_GAMMA:
212       g_value_set_double (value, gamma->gamma);
213       break;
214     default:
215       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
216       break;
217   }
218 }
219
220 static void
221 gst_gamma_calculate_tables (GstGamma * gamma)
222 {
223   gint n;
224   gdouble val;
225   gdouble exp;
226
227   if (gamma->gamma == 1.0) {
228     GST_BASE_TRANSFORM (gamma)->passthrough = TRUE;
229     return;
230   }
231   GST_BASE_TRANSFORM (gamma)->passthrough = FALSE;
232
233   exp = 1.0 / gamma->gamma;
234   for (n = 0; n < 256; n++) {
235     val = n / 255.0;
236     val = pow (val, exp);
237     val = 255.0 * val;
238     gamma->gamma_table[n] = (guint8) floor (val + 0.5);
239   }
240 }
241
242 static void
243 gst_gamma_planar_yuv_ip (GstGamma * gamma, guint8 * data)
244 {
245   gint i, j, height;
246   gint width, row_stride, row_wrap;
247   const guint8 *table = gamma->gamma_table;
248
249   data =
250       data + gst_video_format_get_component_offset (gamma->format, 0,
251       gamma->width, gamma->height);
252
253   width = gst_video_format_get_component_width (gamma->format, 0, gamma->width);
254   height = gst_video_format_get_component_height (gamma->format, 0,
255       gamma->height);
256   row_stride = gst_video_format_get_row_stride (gamma->format, 0, gamma->width);
257   row_wrap = row_stride - width;
258
259   for (i = 0; i < height; i++) {
260     for (j = 0; j < width; j++) {
261       *data = table[*data];
262       data++;
263     }
264     data += row_wrap;
265   }
266 }
267
268 static void
269 gst_gamma_packed_yuv_ip (GstGamma * gamma, guint8 * data)
270 {
271   gint i, j, height;
272   gint width, row_stride, row_wrap;
273   gint pixel_stride;
274   const guint8 *table = gamma->gamma_table;
275
276   data = data + gst_video_format_get_component_offset (gamma->format, 0,
277       gamma->width, gamma->height);
278
279   width = gst_video_format_get_component_width (gamma->format, 0, gamma->width);
280   height = gst_video_format_get_component_height (gamma->format, 0,
281       gamma->height);
282   row_stride = gst_video_format_get_row_stride (gamma->format, 0, gamma->width);
283   pixel_stride = gst_video_format_get_pixel_stride (gamma->format, 0);
284   row_wrap = row_stride - pixel_stride * width;
285
286   for (i = 0; i < height; i++) {
287     for (j = 0; j < width; j++) {
288       *data = table[*data];
289       data += pixel_stride;
290     }
291     data += row_wrap;
292   }
293 }
294
295 static const int cog_ycbcr_to_rgb_matrix_8bit_sdtv[] = {
296   298, 0, 409, -57068,
297   298, -100, -208, 34707,
298   298, 516, 0, -70870,
299 };
300
301 static const gint cog_rgb_to_ycbcr_matrix_8bit_sdtv[] = {
302   66, 129, 25, 4096,
303   -38, -74, 112, 32768,
304   112, -94, -18, 32768,
305 };
306
307 #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)
308
309 static void
310 gst_gamma_packed_rgb_ip (GstGamma * gamma, guint8 * data)
311 {
312   gint i, j, height;
313   gint width, row_stride, row_wrap;
314   gint pixel_stride;
315   const guint8 *table = gamma->gamma_table;
316   gint offsets[3];
317   gint r, g, b;
318   gint y, u, v;
319
320   offsets[0] = gst_video_format_get_component_offset (gamma->format, 0,
321       gamma->width, gamma->height);
322   offsets[1] = gst_video_format_get_component_offset (gamma->format, 1,
323       gamma->width, gamma->height);
324   offsets[2] = gst_video_format_get_component_offset (gamma->format, 2,
325       gamma->width, gamma->height);
326
327   width = gst_video_format_get_component_width (gamma->format, 0, gamma->width);
328   height = gst_video_format_get_component_height (gamma->format, 0,
329       gamma->height);
330   row_stride = gst_video_format_get_row_stride (gamma->format, 0, gamma->width);
331   pixel_stride = gst_video_format_get_pixel_stride (gamma->format, 0);
332   row_wrap = row_stride - pixel_stride * width;
333
334   for (i = 0; i < height; i++) {
335     for (j = 0; j < width; j++) {
336       r = data[offsets[0]];
337       g = data[offsets[1]];
338       b = data[offsets[2]];
339
340       y = APPLY_MATRIX (cog_rgb_to_ycbcr_matrix_8bit_sdtv, 0, r, g, b);
341       u = APPLY_MATRIX (cog_rgb_to_ycbcr_matrix_8bit_sdtv, 1, r, g, b);
342       v = APPLY_MATRIX (cog_rgb_to_ycbcr_matrix_8bit_sdtv, 2, r, g, b);
343
344       y = table[CLAMP (y, 0, 255)];
345       r = APPLY_MATRIX (cog_ycbcr_to_rgb_matrix_8bit_sdtv, 0, y, u, v);
346       g = APPLY_MATRIX (cog_ycbcr_to_rgb_matrix_8bit_sdtv, 1, y, u, v);
347       b = APPLY_MATRIX (cog_ycbcr_to_rgb_matrix_8bit_sdtv, 2, y, u, v);
348
349       data[offsets[0]] = CLAMP (r, 0, 255);
350       data[offsets[1]] = CLAMP (g, 0, 255);
351       data[offsets[2]] = CLAMP (b, 0, 255);
352       data += pixel_stride;
353     }
354     data += row_wrap;
355   }
356 }
357
358 static gboolean
359 gst_gamma_set_caps (GstBaseTransform * base, GstCaps * incaps,
360     GstCaps * outcaps)
361 {
362   GstGamma *gamma = GST_GAMMA (base);
363
364   GST_DEBUG_OBJECT (gamma,
365       "setting caps: in %" GST_PTR_FORMAT " out %" GST_PTR_FORMAT, incaps,
366       outcaps);
367
368   if (!gst_video_format_parse_caps (incaps, &gamma->format, &gamma->width,
369           &gamma->height))
370     goto invalid_caps;
371
372   gamma->size =
373       gst_video_format_get_size (gamma->format, gamma->width, gamma->height);
374
375   switch (gamma->format) {
376     case GST_VIDEO_FORMAT_I420:
377     case GST_VIDEO_FORMAT_YV12:
378     case GST_VIDEO_FORMAT_Y41B:
379     case GST_VIDEO_FORMAT_Y42B:
380     case GST_VIDEO_FORMAT_Y444:
381     case GST_VIDEO_FORMAT_NV12:
382     case GST_VIDEO_FORMAT_NV21:
383       gamma->process = gst_gamma_planar_yuv_ip;
384       break;
385     case GST_VIDEO_FORMAT_YUY2:
386     case GST_VIDEO_FORMAT_UYVY:
387     case GST_VIDEO_FORMAT_AYUV:
388     case GST_VIDEO_FORMAT_YVYU:
389       gamma->process = gst_gamma_packed_yuv_ip;
390       break;
391     case GST_VIDEO_FORMAT_ARGB:
392     case GST_VIDEO_FORMAT_ABGR:
393     case GST_VIDEO_FORMAT_RGBA:
394     case GST_VIDEO_FORMAT_BGRA:
395     case GST_VIDEO_FORMAT_xRGB:
396     case GST_VIDEO_FORMAT_xBGR:
397     case GST_VIDEO_FORMAT_RGBx:
398     case GST_VIDEO_FORMAT_BGRx:
399     case GST_VIDEO_FORMAT_RGB:
400     case GST_VIDEO_FORMAT_BGR:
401       gamma->process = gst_gamma_packed_rgb_ip;
402       break;
403     default:
404       goto invalid_caps;
405       break;
406   }
407
408   return TRUE;
409
410 invalid_caps:
411   GST_ERROR_OBJECT (gamma, "Invalid caps: %" GST_PTR_FORMAT, incaps);
412   return FALSE;
413 }
414
415 static void
416 gst_gamma_before_transform (GstBaseTransform * base, GstBuffer * outbuf)
417 {
418   GstGamma *gamma = GST_GAMMA (base);
419   GstClockTime timestamp, stream_time;
420
421   timestamp = GST_BUFFER_TIMESTAMP (outbuf);
422   stream_time =
423       gst_segment_to_stream_time (&base->segment, GST_FORMAT_TIME, timestamp);
424
425   GST_DEBUG_OBJECT (gamma, "sync to %" GST_TIME_FORMAT,
426       GST_TIME_ARGS (timestamp));
427
428   if (GST_CLOCK_TIME_IS_VALID (stream_time))
429     gst_object_sync_values (G_OBJECT (gamma), stream_time);
430 }
431
432 static GstFlowReturn
433 gst_gamma_transform_ip (GstBaseTransform * base, GstBuffer * outbuf)
434 {
435   GstGamma *gamma = GST_GAMMA (base);
436   guint8 *data;
437   guint size;
438
439   if (!gamma->process)
440     goto not_negotiated;
441
442   if (base->passthrough)
443     goto done;
444
445   data = GST_BUFFER_DATA (outbuf);
446   size = GST_BUFFER_SIZE (outbuf);
447
448   if (size != gamma->size)
449     goto wrong_size;
450
451   GST_OBJECT_LOCK (gamma);
452   gamma->process (gamma, data);
453   GST_OBJECT_UNLOCK (gamma);
454
455 done:
456   return GST_FLOW_OK;
457
458   /* ERRORS */
459 wrong_size:
460   {
461     GST_ELEMENT_ERROR (gamma, STREAM, FORMAT,
462         (NULL), ("Invalid buffer size %d, expected %d", size, gamma->size));
463     return GST_FLOW_ERROR;
464   }
465 not_negotiated:
466   {
467     GST_ERROR_OBJECT (gamma, "Not negotiated yet");
468     return GST_FLOW_NOT_NEGOTIATED;
469   }
470 }