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