Merge branch 'master' into 0.11
[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_info (GstVideoFilter * vfilter, GstCaps * incaps,
98     GstVideoInfo * in_info, GstCaps * outcaps, GstVideoInfo * out_info);
99 static GstFlowReturn gst_gamma_transform_frame_ip (GstVideoFilter * vfilter,
100     GstVideoFrame * frame);
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   GstVideoFilterClass *vfilter_class = (GstVideoFilterClass *) g_class;
115
116   GST_DEBUG_CATEGORY_INIT (gamma_debug, "gamma", 0, "gamma");
117
118   gobject_class->set_property = gst_gamma_set_property;
119   gobject_class->get_property = gst_gamma_get_property;
120
121   g_object_class_install_property (gobject_class, PROP_GAMMA,
122       g_param_spec_double ("gamma", "Gamma", "gamma",
123           0.01, 10, DEFAULT_PROP_GAMMA,
124           GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE));
125
126   gst_element_class_set_details_simple (gstelement_class,
127       "Video gamma correction", "Filter/Effect/Video",
128       "Adjusts gamma on a video stream", "Arwed v. Merkatz <v.merkatz@gmx.net");
129
130   gst_element_class_add_pad_template (gstelement_class,
131       gst_static_pad_template_get (&gst_gamma_sink_template));
132   gst_element_class_add_pad_template (gstelement_class,
133       gst_static_pad_template_get (&gst_gamma_src_template));
134
135   trans_class->before_transform =
136       GST_DEBUG_FUNCPTR (gst_gamma_before_transform);
137
138   vfilter_class->set_info = GST_DEBUG_FUNCPTR (gst_gamma_set_info);
139   vfilter_class->transform_frame_ip =
140       GST_DEBUG_FUNCPTR (gst_gamma_transform_frame_ip);
141 }
142
143 static void
144 gst_gamma_init (GstGamma * gamma)
145 {
146   /* properties */
147   gamma->gamma = DEFAULT_PROP_GAMMA;
148   gst_gamma_calculate_tables (gamma);
149 }
150
151 static void
152 gst_gamma_set_property (GObject * object, guint prop_id, const GValue * value,
153     GParamSpec * pspec)
154 {
155   GstGamma *gamma = GST_GAMMA (object);
156
157   switch (prop_id) {
158     case PROP_GAMMA:{
159       gdouble val = g_value_get_double (value);
160
161       GST_DEBUG_OBJECT (gamma, "Changing gamma from %lf to %lf", gamma->gamma,
162           val);
163       GST_OBJECT_LOCK (gamma);
164       gamma->gamma = val;
165       gst_gamma_calculate_tables (gamma);
166       GST_OBJECT_UNLOCK (gamma);
167       break;
168     }
169     default:
170       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
171       break;
172   }
173 }
174
175 static void
176 gst_gamma_get_property (GObject * object, guint prop_id, GValue * value,
177     GParamSpec * pspec)
178 {
179   GstGamma *gamma = GST_GAMMA (object);
180
181   switch (prop_id) {
182     case PROP_GAMMA:
183       g_value_set_double (value, gamma->gamma);
184       break;
185     default:
186       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
187       break;
188   }
189 }
190
191 static void
192 gst_gamma_calculate_tables (GstGamma * gamma)
193 {
194   gint n;
195   gdouble val;
196   gdouble exp;
197
198   if (gamma->gamma == 1.0) {
199     GST_BASE_TRANSFORM (gamma)->passthrough = TRUE;
200     return;
201   }
202   GST_BASE_TRANSFORM (gamma)->passthrough = FALSE;
203
204   exp = 1.0 / gamma->gamma;
205   for (n = 0; n < 256; n++) {
206     val = n / 255.0;
207     val = pow (val, exp);
208     val = 255.0 * val;
209     gamma->gamma_table[n] = (guint8) floor (val + 0.5);
210   }
211 }
212
213 static void
214 gst_gamma_planar_yuv_ip (GstGamma * gamma, GstVideoFrame * frame)
215 {
216   gint i, j, height;
217   gint width, stride, row_wrap;
218   const guint8 *table = gamma->gamma_table;
219   guint8 *data;
220
221   data = GST_VIDEO_FRAME_COMP_DATA (frame, 0);
222   stride = GST_VIDEO_FRAME_COMP_STRIDE (frame, 0);
223   width = GST_VIDEO_FRAME_COMP_WIDTH (frame, 0);
224   height = GST_VIDEO_FRAME_COMP_HEIGHT (frame, 0);
225   row_wrap = stride - width;
226
227   for (i = 0; i < height; i++) {
228     for (j = 0; j < width; j++) {
229       *data = table[*data];
230       data++;
231     }
232     data += row_wrap;
233   }
234 }
235
236 static void
237 gst_gamma_packed_yuv_ip (GstGamma * gamma, GstVideoFrame * frame)
238 {
239   gint i, j, height;
240   gint width, stride, row_wrap;
241   gint pixel_stride;
242   const guint8 *table = gamma->gamma_table;
243   guint8 *data;
244
245   data = GST_VIDEO_FRAME_COMP_DATA (frame, 0);
246   stride = GST_VIDEO_FRAME_COMP_STRIDE (frame, 0);
247   width = GST_VIDEO_FRAME_COMP_WIDTH (frame, 0);
248   height = GST_VIDEO_FRAME_COMP_HEIGHT (frame, 0);
249   pixel_stride = GST_VIDEO_FRAME_COMP_PSTRIDE (frame, 0);
250   row_wrap = stride - pixel_stride * width;
251
252   for (i = 0; i < height; i++) {
253     for (j = 0; j < width; j++) {
254       *data = table[*data];
255       data += pixel_stride;
256     }
257     data += row_wrap;
258   }
259 }
260
261 static const int cog_ycbcr_to_rgb_matrix_8bit_sdtv[] = {
262   298, 0, 409, -57068,
263   298, -100, -208, 34707,
264   298, 516, 0, -70870,
265 };
266
267 static const gint cog_rgb_to_ycbcr_matrix_8bit_sdtv[] = {
268   66, 129, 25, 4096,
269   -38, -74, 112, 32768,
270   112, -94, -18, 32768,
271 };
272
273 #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)
274
275 static void
276 gst_gamma_packed_rgb_ip (GstGamma * gamma, GstVideoFrame * frame)
277 {
278   gint i, j, height;
279   gint width, stride, row_wrap;
280   gint pixel_stride;
281   const guint8 *table = gamma->gamma_table;
282   gint offsets[3];
283   gint r, g, b;
284   gint y, u, v;
285   guint8 *data;
286
287   data = GST_VIDEO_FRAME_PLANE_DATA (frame, 0);
288   stride = GST_VIDEO_FRAME_PLANE_STRIDE (frame, 0);
289   width = GST_VIDEO_FRAME_COMP_WIDTH (frame, 0);
290   height = GST_VIDEO_FRAME_COMP_HEIGHT (frame, 0);
291
292   offsets[0] = GST_VIDEO_FRAME_COMP_OFFSET (frame, 0);
293   offsets[1] = GST_VIDEO_FRAME_COMP_OFFSET (frame, 1);
294   offsets[2] = GST_VIDEO_FRAME_COMP_OFFSET (frame, 2);
295
296   pixel_stride = GST_VIDEO_FRAME_COMP_PSTRIDE (frame, 0);
297   row_wrap = stride - pixel_stride * width;
298
299   for (i = 0; i < height; i++) {
300     for (j = 0; j < width; j++) {
301       r = data[offsets[0]];
302       g = data[offsets[1]];
303       b = data[offsets[2]];
304
305       y = APPLY_MATRIX (cog_rgb_to_ycbcr_matrix_8bit_sdtv, 0, r, g, b);
306       u = APPLY_MATRIX (cog_rgb_to_ycbcr_matrix_8bit_sdtv, 1, r, g, b);
307       v = APPLY_MATRIX (cog_rgb_to_ycbcr_matrix_8bit_sdtv, 2, r, g, b);
308
309       y = table[CLAMP (y, 0, 255)];
310       r = APPLY_MATRIX (cog_ycbcr_to_rgb_matrix_8bit_sdtv, 0, y, u, v);
311       g = APPLY_MATRIX (cog_ycbcr_to_rgb_matrix_8bit_sdtv, 1, y, u, v);
312       b = APPLY_MATRIX (cog_ycbcr_to_rgb_matrix_8bit_sdtv, 2, y, u, v);
313
314       data[offsets[0]] = CLAMP (r, 0, 255);
315       data[offsets[1]] = CLAMP (g, 0, 255);
316       data[offsets[2]] = CLAMP (b, 0, 255);
317       data += pixel_stride;
318     }
319     data += row_wrap;
320   }
321 }
322
323 static gboolean
324 gst_gamma_set_info (GstVideoFilter * vfilter, GstCaps * incaps,
325     GstVideoInfo * in_info, GstCaps * outcaps, GstVideoInfo * out_info)
326 {
327   GstGamma *gamma = GST_GAMMA (vfilter);
328
329   GST_DEBUG_OBJECT (gamma,
330       "setting caps: in %" GST_PTR_FORMAT " out %" GST_PTR_FORMAT, incaps,
331       outcaps);
332
333   switch (GST_VIDEO_INFO_FORMAT (in_info)) {
334     case GST_VIDEO_FORMAT_I420:
335     case GST_VIDEO_FORMAT_YV12:
336     case GST_VIDEO_FORMAT_Y41B:
337     case GST_VIDEO_FORMAT_Y42B:
338     case GST_VIDEO_FORMAT_Y444:
339     case GST_VIDEO_FORMAT_NV12:
340     case GST_VIDEO_FORMAT_NV21:
341       gamma->process = gst_gamma_planar_yuv_ip;
342       break;
343     case GST_VIDEO_FORMAT_YUY2:
344     case GST_VIDEO_FORMAT_UYVY:
345     case GST_VIDEO_FORMAT_AYUV:
346     case GST_VIDEO_FORMAT_YVYU:
347       gamma->process = gst_gamma_packed_yuv_ip;
348       break;
349     case GST_VIDEO_FORMAT_ARGB:
350     case GST_VIDEO_FORMAT_ABGR:
351     case GST_VIDEO_FORMAT_RGBA:
352     case GST_VIDEO_FORMAT_BGRA:
353     case GST_VIDEO_FORMAT_xRGB:
354     case GST_VIDEO_FORMAT_xBGR:
355     case GST_VIDEO_FORMAT_RGBx:
356     case GST_VIDEO_FORMAT_BGRx:
357     case GST_VIDEO_FORMAT_RGB:
358     case GST_VIDEO_FORMAT_BGR:
359       gamma->process = gst_gamma_packed_rgb_ip;
360       break;
361     default:
362       goto invalid_caps;
363       break;
364   }
365   return TRUE;
366
367   /* ERRORS */
368 invalid_caps:
369   {
370     GST_ERROR_OBJECT (gamma, "Invalid caps: %" GST_PTR_FORMAT, incaps);
371     return FALSE;
372   }
373 }
374
375 static void
376 gst_gamma_before_transform (GstBaseTransform * base, GstBuffer * outbuf)
377 {
378   GstGamma *gamma = GST_GAMMA (base);
379   GstClockTime timestamp, stream_time;
380
381   timestamp = GST_BUFFER_TIMESTAMP (outbuf);
382   stream_time =
383       gst_segment_to_stream_time (&base->segment, GST_FORMAT_TIME, timestamp);
384
385   GST_DEBUG_OBJECT (gamma, "sync to %" GST_TIME_FORMAT,
386       GST_TIME_ARGS (timestamp));
387
388   if (GST_CLOCK_TIME_IS_VALID (stream_time))
389     gst_object_sync_values (GST_OBJECT (gamma), stream_time);
390 }
391
392 static GstFlowReturn
393 gst_gamma_transform_frame_ip (GstVideoFilter * vfilter, GstVideoFrame * frame)
394 {
395   GstGamma *gamma = GST_GAMMA (vfilter);
396
397   if (!gamma->process)
398     goto not_negotiated;
399
400   if (GST_BASE_TRANSFORM (vfilter)->passthrough)
401     goto done;
402
403   GST_OBJECT_LOCK (gamma);
404   gamma->process (gamma, frame);
405   GST_OBJECT_UNLOCK (gamma);
406
407 done:
408   return GST_FLOW_OK;
409
410   /* ERRORS */
411 not_negotiated:
412   {
413     GST_ERROR_OBJECT (gamma, "Not negotiated yet");
414     return GST_FLOW_NOT_NEGOTIATED;
415   }
416 }