effect: Add optv effect filter from the effectv project
[platform/upstream/gst-plugins-good.git] / gst / effectv / gstop.c
1 /* GStreamer
2  * Copyright (C) <2009> Sebastian Dröge <sebastian.droege@collabora.co.uk>
3  *
4  * EffecTV - Realtime Digital Video Effector
5  * Copyright (C) 2001-2006 FUKUCHI Kentaro
6  *
7  * OpTV - Optical art meets real-time video effect.
8  * Copyright (C) 2004-2005 FUKUCHI Kentaro
9  *
10  * EffecTV is free software. This library is free software;
11  * you can redistribute it and/or
12  * modify it under the terms of the GNU Library General Public
13  * License as published by the Free Software Foundation; either
14  * version 2 of the License, or (at your option) any later version.
15  *
16  * This library is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
19  * Library General Public License for more details.
20  *
21  * You should have received a copy of the GNU Library General Public
22  * License along with this library; if not, write to the
23  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
24  * Boston, MA 02111-1307, USA.
25  */
26
27 /**
28  * SECTION:element-optv
29  *
30  * Traditional black-white optical animation is now resurrected as a
31  * real-time video effect. Input images are binarized and combined with
32  * various optical pattern.
33  *
34  * <refsect2>
35  * <title>Example launch line</title>
36  * |[
37  * gst-launch -v videotestsrc ! optv ! ffmpegcolorspace ! autovideosink
38  * ]| This pipeline shows the effect of optv on a test stream.
39  * </refsect2>
40  */
41
42 #ifdef HAVE_CONFIG_H
43 #include "config.h"
44 #endif
45
46 #include <math.h>
47 #include <string.h>
48
49 #include "gstop.h"
50 #include "gsteffectv.h"
51
52 #include <gst/video/video.h>
53 #include <gst/controller/gstcontroller.h>
54
55 enum
56 {
57   OP_SPIRAL1 = 0,
58   OP_SPIRAL2,
59   OP_PARABOLA,
60   OP_HSTRIPE
61 };
62
63 #define GST_TYPE_OPTV_MODE (gst_optv_mode_get_type())
64 static GType
65 gst_optv_mode_get_type (void)
66 {
67   static GType type = 0;
68
69   static const GEnumValue enumvalue[] = {
70     {OP_SPIRAL1, "Maelstrom", "maelstrom"},
71     {OP_SPIRAL2, "Radiation", "radiation"},
72     {OP_PARABOLA, "Horizontal Stripes",
73         "horizontal-stripes"},
74     {OP_HSTRIPE, "Vertical Stripes", "vertical-stripes"},
75     {0, NULL, NULL},
76   };
77
78   if (!type) {
79     type = g_enum_register_static ("GstOpTVMode", enumvalue);
80   }
81   return type;
82 }
83
84 #define DEFAULT_MODE OP_SPIRAL1
85 #define DEFAULT_SPEED 16
86 #define DEFAULT_THRESHOLD 60
87
88 enum
89 {
90   PROP_0,
91   PROP_MODE,
92   PROP_SPEED,
93   PROP_THRESHOLD
94 };
95
96 static guint32 palette[256];
97
98 GST_BOILERPLATE (GstOpTV, gst_optv, GstVideoFilter, GST_TYPE_VIDEO_FILTER);
99
100 static GstStaticPadTemplate gst_optv_src_template =
101     GST_STATIC_PAD_TEMPLATE ("src",
102     GST_PAD_SRC,
103     GST_PAD_ALWAYS,
104     GST_STATIC_CAPS (GST_VIDEO_CAPS_BGRx "; " GST_VIDEO_CAPS_RGBx)
105     );
106
107 static GstStaticPadTemplate gst_optv_sink_template =
108     GST_STATIC_PAD_TEMPLATE ("sink",
109     GST_PAD_SINK,
110     GST_PAD_ALWAYS,
111     GST_STATIC_CAPS (GST_VIDEO_CAPS_BGRx "; " GST_VIDEO_CAPS_RGBx)
112     );
113
114 static void
115 initPalette (void)
116 {
117   gint i;
118   guint8 v;
119
120   for (i = 0; i < 112; i++) {
121     palette[i] = 0;
122     palette[i + 128] = 0xffffff;
123   }
124   for (i = 0; i < 16; i++) {
125     v = 16 * (i + 1) - 1;
126     palette[i + 112] = (v << 16) | (v << 8) | v;
127     v = 255 - v;
128     palette[i + 240] = (v << 16) | (v << 8) | v;
129   }
130 }
131
132 static void
133 setOpmap (gint8 * opmap[4], gint width, gint height)
134 {
135   gint i, j, x, y;
136 #ifndef PS2
137   gdouble xx, yy, r, at, rr;
138 #else
139   gfloat xx, yy, r, at, rr;
140 #endif
141   gint sci;
142
143   sci = 640 / width;
144   i = 0;
145   for (y = 0; y < height; y++) {
146     yy = (gdouble) (y - height / 2) / width;
147     for (x = 0; x < width; x++) {
148       xx = (gdouble) x / width - 0.5;
149 #ifndef PS2
150       r = sqrt (xx * xx + yy * yy);
151       at = atan2 (xx, yy);
152 #else
153       r = sqrtf (xx * xx + yy * yy);
154       at = atan2f (xx, yy);
155 #endif
156
157       opmap[OP_SPIRAL1][i] = ((guint)
158           ((at / M_PI * 256) + (r * 4000))) & 255;
159
160       j = r * 300 / 32;
161       rr = r * 300 - j * 32;
162       j *= 64;
163       j += (rr > 28) ? (rr - 28) * 16 : 0;
164       opmap[OP_SPIRAL2][i] = ((guint)
165           ((at / M_PI * 4096) + (r * 1600) - j)) & 255;
166
167       opmap[OP_PARABOLA][i] =
168           ((guint) (yy / (xx * xx * 0.3 + 0.1) * 400)) & 255;
169       opmap[OP_HSTRIPE][i] = x * 8 * sci;
170       i++;
171     }
172   }
173 }
174
175 /* Taken from effectv/image.c */
176 /* Y value filters */
177 static void
178 image_y_over (guint32 * src, guint8 * diff, gint y_threshold, gint video_area)
179 {
180   gint i;
181   gint R, G, B, v;
182   guint8 *p = diff;
183
184   for (i = video_area; i > 0; i--) {
185     R = ((*src) & 0xff0000) >> (16 - 1);
186     G = ((*src) & 0xff00) >> (8 - 2);
187     B = (*src) & 0xff;
188     v = y_threshold * 7 - (R + G + B);
189     *p = (guint8) (v >> 24);
190     src++;
191     p++;
192   }
193 }
194
195 static GstFlowReturn
196 gst_optv_transform (GstBaseTransform * trans, GstBuffer * in, GstBuffer * out)
197 {
198   GstOpTV *filter = GST_OPTV (trans);
199   guint32 *src, *dest;
200   GstFlowReturn ret = GST_FLOW_OK;
201   gint8 *p;
202   guint8 *diff;
203   gint x, y;
204   GstClockTime timestamp, stream_time;
205
206   timestamp = GST_BUFFER_TIMESTAMP (in);
207   stream_time =
208       gst_segment_to_stream_time (&trans->segment, GST_FORMAT_TIME, timestamp);
209
210   GST_DEBUG_OBJECT (filter, "sync to %" GST_TIME_FORMAT,
211       GST_TIME_ARGS (timestamp));
212
213   if (GST_CLOCK_TIME_IS_VALID (stream_time))
214     gst_object_sync_values (G_OBJECT (filter), stream_time);
215
216   src = (guint32 *) GST_BUFFER_DATA (in);
217   dest = (guint32 *) GST_BUFFER_DATA (out);
218
219   if (G_UNLIKELY (filter->opmap[0] == NULL))
220     return GST_FLOW_NOT_NEGOTIATED;
221
222   switch (filter->mode) {
223     default:
224     case 0:
225       p = filter->opmap[OP_SPIRAL1];
226       break;
227     case 1:
228       p = filter->opmap[OP_SPIRAL2];
229       break;
230     case 2:
231       p = filter->opmap[OP_PARABOLA];
232       break;
233     case 3:
234       p = filter->opmap[OP_HSTRIPE];
235       break;
236   }
237
238   filter->phase -= filter->speed;
239
240   diff = filter->diff;
241   image_y_over (src, diff, filter->threshold, filter->width * filter->height);
242
243   for (y = 0; y < filter->height; y++) {
244     for (x = 0; x < filter->width; x++) {
245       *dest++ = palette[(((guint8) (*p + filter->phase)) ^ *diff++) & 255];
246       p++;
247     }
248   }
249
250   return ret;
251 }
252
253 static gboolean
254 gst_optv_set_caps (GstBaseTransform * btrans, GstCaps * incaps,
255     GstCaps * outcaps)
256 {
257   GstOpTV *filter = GST_OPTV (btrans);
258   GstStructure *structure;
259   gboolean ret = FALSE;
260
261   structure = gst_caps_get_structure (incaps, 0);
262
263   if (gst_structure_get_int (structure, "width", &filter->width) &&
264       gst_structure_get_int (structure, "height", &filter->height)) {
265     gint i;
266
267     for (i = 0; i < 4; i++) {
268       if (filter->opmap[i])
269         g_free (filter->opmap[i]);
270       filter->opmap[i] = g_new (gint8, filter->width * filter->height);
271     }
272     setOpmap (filter->opmap, filter->width, filter->height);
273
274     if (filter->diff)
275       g_free (filter->diff);
276     filter->diff = g_new (guint8, filter->width * filter->height);
277
278     ret = TRUE;
279   }
280
281   return ret;
282 }
283
284 static gboolean
285 gst_optv_start (GstBaseTransform * trans)
286 {
287   GstOpTV *filter = GST_OPTV (trans);
288
289   filter->phase = 0;
290
291   return TRUE;
292 }
293
294 static void
295 gst_optv_finalize (GObject * object)
296 {
297   GstOpTV *filter = GST_OPTV (object);
298
299   if (filter->opmap[0]) {
300     gint i;
301
302     for (i = 0; i < 4; i++) {
303       if (filter->opmap[i])
304         g_free (filter->opmap[i]);
305       filter->opmap[i] = NULL;
306     }
307   }
308
309   if (filter->diff)
310     g_free (filter->diff);
311   filter->diff = NULL;
312
313   G_OBJECT_CLASS (parent_class)->finalize (object);
314 }
315
316 static void
317 gst_optv_set_property (GObject * object, guint prop_id, const GValue * value,
318     GParamSpec * pspec)
319 {
320   GstOpTV *filter = GST_OPTV (object);
321
322   switch (prop_id) {
323     case PROP_MODE:
324       filter->mode = g_value_get_enum (value);
325       break;
326     case PROP_SPEED:
327       filter->speed = g_value_get_int (value);
328       break;
329     case PROP_THRESHOLD:
330       filter->threshold = g_value_get_int (value);
331       break;
332     default:
333       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
334       break;
335   }
336 }
337
338 static void
339 gst_optv_get_property (GObject * object, guint prop_id, GValue * value,
340     GParamSpec * pspec)
341 {
342   GstOpTV *filter = GST_OPTV (object);
343
344   switch (prop_id) {
345     case PROP_MODE:
346       g_value_set_enum (value, filter->mode);
347       break;
348     case PROP_SPEED:
349       g_value_set_int (value, filter->speed);
350       break;
351     case PROP_THRESHOLD:
352       g_value_set_int (value, filter->threshold);
353       break;
354     default:
355       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
356       break;
357   }
358 }
359
360 static void
361 gst_optv_base_init (gpointer g_class)
362 {
363   GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
364
365   gst_element_class_set_details_simple (element_class, "OpTV effect",
366       "Filter/Effect/Video",
367       "Optical art meets real-time video effect",
368       "FUKUCHI, Kentarou <fukuchi@users.sourceforge.net>, "
369       "Sebastian Dröge <sebastian.droege@collabora.co.uk>");
370
371   gst_element_class_add_pad_template (element_class,
372       gst_static_pad_template_get (&gst_optv_sink_template));
373   gst_element_class_add_pad_template (element_class,
374       gst_static_pad_template_get (&gst_optv_src_template));
375 }
376
377 static void
378 gst_optv_class_init (GstOpTVClass * klass)
379 {
380   GObjectClass *gobject_class = (GObjectClass *) klass;
381   GstBaseTransformClass *trans_class = (GstBaseTransformClass *) klass;
382
383   gobject_class->set_property = gst_optv_set_property;
384   gobject_class->get_property = gst_optv_get_property;
385
386   gobject_class->finalize = gst_optv_finalize;
387
388   g_object_class_install_property (gobject_class, PROP_MODE,
389       g_param_spec_enum ("mode", "Mode",
390           "Mode", GST_TYPE_OPTV_MODE, DEFAULT_MODE,
391           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
392
393   g_object_class_install_property (gobject_class, PROP_SPEED,
394       g_param_spec_int ("speed", "Speed",
395           "Effect speed", G_MININT, G_MAXINT, DEFAULT_SPEED,
396           GST_PARAM_CONTROLLABLE | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
397
398   g_object_class_install_property (gobject_class, PROP_THRESHOLD,
399       g_param_spec_int ("threshold", "Threshold",
400           "Luma threshold", G_MININT, G_MAXINT, DEFAULT_THRESHOLD,
401           GST_PARAM_CONTROLLABLE | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
402
403   trans_class->set_caps = GST_DEBUG_FUNCPTR (gst_optv_set_caps);
404   trans_class->transform = GST_DEBUG_FUNCPTR (gst_optv_transform);
405   trans_class->start = GST_DEBUG_FUNCPTR (gst_optv_start);
406
407   initPalette ();
408 }
409
410 static void
411 gst_optv_init (GstOpTV * filter, GstOpTVClass * klass)
412 {
413   filter->speed = DEFAULT_SPEED;
414   filter->mode = DEFAULT_MODE;
415   filter->threshold = DEFAULT_THRESHOLD;
416 }