controller: port to new controller location and api
[platform/upstream/gstreamer.git] / gst / effectv / gstripple.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  * RippleTV - Water ripple effect.
8  * Copyright (C) 2001-2002 FUKUCHI Kentaro
9  *
10  * This combines the RippleTV and BaltanTV effects, which are
11  * very similar. BaltanTV is used if the feedback property is set
12  * to TRUE, otherwise RippleTV is used.
13  *
14  * EffecTV is free software. This library is free software;
15  * you can redistribute it and/or
16  * modify it under the terms of the GNU Library General Public
17  * License as published by the Free Software Foundation; either
18  * version 2 of the License, or (at your option) any later version.
19  *
20  * This library is distributed in the hope that it will be useful,
21  * but WITHOUT ANY WARRANTY; without even the implied warranty of
22  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
23  * Library General Public License for more details.
24  *
25  * You should have received a copy of the GNU Library General Public
26  * License along with this library; if not, write to the
27  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
28  * Boston, MA 02111-1307, USA.
29  */
30
31 /**
32  * SECTION:element-rippletv
33  *
34  * RippleTV does ripple mark effect on the video input. The ripple is caused
35  * by motion or random rain drops.
36  *
37  * <refsect2>
38  * <title>Example launch line</title>
39  * |[
40  * gst-launch -v videotestsrc ! rippletv ! videoconvert ! autovideosink
41  * ]| This pipeline shows the effect of rippletv on a test stream.
42  * </refsect2>
43  */
44
45 #ifdef HAVE_CONFIG_H
46 #include "config.h"
47 #endif
48
49 #include <math.h>
50 #include <string.h>
51
52 #include "gstripple.h"
53 #include "gsteffectv.h"
54
55 #define DEFAULT_MODE 0
56
57 enum
58 {
59   PROP_0,
60   PROP_RESET,
61   PROP_MODE
62 };
63
64 static gint sqrtable[256];
65
66 #define GST_TYPE_RIPPLETV_MODE (gst_rippletv_mode_get_type())
67 static GType
68 gst_rippletv_mode_get_type (void)
69 {
70   static GType type = 0;
71
72   static const GEnumValue enumvalue[] = {
73     {0, "Motion Detection", "motion-detection"},
74     {1, "Rain", "rain"},
75     {0, NULL, NULL},
76   };
77
78   if (!type) {
79     type = g_enum_register_static ("GstRippleTVMode", enumvalue);
80   }
81   return type;
82 }
83
84 #define gst_rippletv_parent_class parent_class
85 G_DEFINE_TYPE (GstRippleTV, gst_rippletv, GST_TYPE_VIDEO_FILTER);
86
87 static GstStaticPadTemplate gst_rippletv_src_template =
88 GST_STATIC_PAD_TEMPLATE ("src",
89     GST_PAD_SRC,
90     GST_PAD_ALWAYS,
91     GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE ("{ BGRx, RGBx, xBGR, xRGB }"))
92     );
93
94 static GstStaticPadTemplate gst_rippletv_sink_template =
95 GST_STATIC_PAD_TEMPLATE ("sink",
96     GST_PAD_SINK,
97     GST_PAD_ALWAYS,
98     GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE ("{ BGRx, RGBx, xBGR, xRGB }"))
99     );
100
101 static const gint point = 16;
102 static const gint impact = 2;
103 static const gint decay = 8;
104 static const gint loopnum = 2;
105
106 static void
107 setTable (void)
108 {
109   gint i;
110
111   for (i = 0; i < 128; i++) {
112     sqrtable[i] = i * i;
113   }
114   for (i = 1; i <= 128; i++) {
115     sqrtable[256 - i] = -i * i;
116   }
117 }
118
119 static void
120 image_bgset_y (guint32 * src, gint16 * background, gint video_area)
121 {
122   gint i;
123   gint R, G, B;
124   guint32 *p;
125   gint16 *q;
126
127   p = src;
128   q = background;
129   for (i = 0; i < video_area; i++) {
130     R = ((*p) & 0xff0000) >> (16 - 1);
131     G = ((*p) & 0xff00) >> (8 - 2);
132     B = (*p) & 0xff;
133     *q = (gint16) (R + G + B);
134     p++;
135     q++;
136   }
137 }
138
139 static gint
140 setBackground (GstRippleTV * filter, guint32 * src)
141 {
142   image_bgset_y (src, filter->background,
143       GST_VIDEO_INFO_WIDTH (&filter->info) *
144       GST_VIDEO_INFO_HEIGHT (&filter->info));
145   filter->bg_is_set = TRUE;
146
147   return 0;
148 }
149
150 static void
151 image_bgsubtract_update_y (guint32 * src, gint16 * background, guint8 * diff,
152     gint video_area)
153 {
154   gint i;
155   gint R, G, B;
156   guint32 *p;
157   gint16 *q;
158   guint8 *r;
159   gint v;
160
161   p = src;
162   q = background;
163   r = diff;
164   for (i = 0; i < video_area; i++) {
165     R = ((*p) & 0xff0000) >> (16 - 1);
166     G = ((*p) & 0xff00) >> (8 - 2);
167     B = (*p) & 0xff;
168     v = (R + G + B) - (gint) (*q);
169     *q = (gint16) (R + G + B);
170     *r = ((v + 70 * 7) >> 24) | ((70 * 7 - v) >> 24);
171
172     p++;
173     q++;
174     r++;
175   }
176 }
177
178 static void
179 motiondetect (GstRippleTV * filter, guint32 * src)
180 {
181   guint8 *diff = filter->diff;
182   gint width, height;
183   gint *p, *q;
184   gint x, y, h;
185
186   width = GST_VIDEO_INFO_WIDTH (&filter->info);
187   height = GST_VIDEO_INFO_HEIGHT (&filter->info);
188
189   if (!filter->bg_is_set)
190     setBackground (filter, src);
191
192   image_bgsubtract_update_y (src, filter->background, filter->diff,
193       width * height);
194   p = filter->map1 + filter->map_w + 1;
195   q = filter->map2 + filter->map_w + 1;
196   diff += width + 2;
197
198   for (y = filter->map_h - 2; y > 0; y--) {
199     for (x = filter->map_w - 2; x > 0; x--) {
200       h = (gint) * diff + (gint) * (diff + 1) + (gint) * (diff + width) +
201           (gint) * (diff + width + 1);
202       if (h > 0) {
203         *p = h << (point + impact - 8);
204         *q = *p;
205       }
206       p++;
207       q++;
208       diff += 2;
209     }
210     diff += width + 2;
211     p += 2;
212     q += 2;
213   }
214 }
215
216 static inline void
217 drop (gint power, gint * map1, gint * map2, gint map_w, gint map_h)
218 {
219   gint x, y;
220   gint *p, *q;
221
222   x = fastrand () % (map_w - 4) + 2;
223   y = fastrand () % (map_h - 4) + 2;
224   p = map1 + y * map_w + x;
225   q = map2 + y * map_w + x;
226   *p = power;
227   *q = power;
228   *(p - map_w) = *(p - 1) = *(p + 1) = *(p + map_w) = power / 2;
229   *(p - map_w - 1) = *(p - map_w + 1) = *(p + map_w - 1) = *(p + map_w + 1) =
230       power / 4;
231   *(q - map_w) = *(q - 1) = *(q + 1) = *(q + map_w) = power / 2;
232   *(q - map_w - 1) = *(q - map_w + 1) = *(q + map_w - 1) = *(p + map_w + 1) =
233       power / 4;
234 }
235
236 static void
237 raindrop (GstRippleTV * filter)
238 {
239   gint i;
240
241   if (filter->period == 0) {
242     switch (filter->rain_stat) {
243       case 0:
244         filter->period = (fastrand () >> 23) + 100;
245         filter->drop_prob = 0;
246         filter->drop_prob_increment = 0x00ffffff / filter->period;
247         filter->drop_power = (-(fastrand () >> 28) - 2) << point;
248         filter->drops_per_frame_max = 2 << (fastrand () >> 30); // 2,4,8 or 16
249         filter->rain_stat = 1;
250         break;
251       case 1:
252         filter->drop_prob = 0x00ffffff;
253         filter->drops_per_frame = 1;
254         filter->drop_prob_increment = 1;
255         filter->period = (filter->drops_per_frame_max - 1) * 16;
256         filter->rain_stat = 2;
257         break;
258       case 2:
259         filter->period = (fastrand () >> 22) + 1000;
260         filter->drop_prob_increment = 0;
261         filter->rain_stat = 3;
262         break;
263       case 3:
264         filter->period = (filter->drops_per_frame_max - 1) * 16;
265         filter->drop_prob_increment = -1;
266         filter->rain_stat = 4;
267         break;
268       case 4:
269         filter->period = (fastrand () >> 24) + 60;
270         filter->drop_prob_increment = -(filter->drop_prob / filter->period);
271         filter->rain_stat = 5;
272         break;
273       case 5:
274       default:
275         filter->period = (fastrand () >> 23) + 500;
276         filter->drop_prob = 0;
277         filter->rain_stat = 0;
278         break;
279     }
280   }
281   switch (filter->rain_stat) {
282     default:
283     case 0:
284       break;
285     case 1:
286     case 5:
287       if ((fastrand () >> 8) < filter->drop_prob) {
288         drop (filter->drop_power, filter->map1, filter->map2, filter->map_w,
289             filter->map_h);
290       }
291       filter->drop_prob += filter->drop_prob_increment;
292       break;
293     case 2:
294     case 3:
295     case 4:
296       for (i = filter->drops_per_frame / 16; i > 0; i--) {
297         drop (filter->drop_power, filter->map1, filter->map2, filter->map_w,
298             filter->map_h);
299       }
300       filter->drops_per_frame += filter->drop_prob_increment;
301       break;
302   }
303   filter->period--;
304 }
305
306 static GstFlowReturn
307 gst_rippletv_transform (GstBaseTransform * trans, GstBuffer * in,
308     GstBuffer * out)
309 {
310   GstRippleTV *filter = GST_RIPPLETV (trans);
311   guint32 *src, *dest;
312   GstVideoFrame in_frame, out_frame;
313   gint x, y, i;
314   gint dx, dy;
315   gint h, v;
316   gint width, height;
317   gint *p, *q, *r;
318   gint8 *vp;
319   GstClockTime timestamp, stream_time;
320
321   timestamp = GST_BUFFER_TIMESTAMP (in);
322   stream_time =
323       gst_segment_to_stream_time (&trans->segment, GST_FORMAT_TIME, timestamp);
324
325   GST_DEBUG_OBJECT (filter, "sync to %" GST_TIME_FORMAT,
326       GST_TIME_ARGS (timestamp));
327
328   if (GST_CLOCK_TIME_IS_VALID (stream_time))
329     gst_object_sync_values (GST_OBJECT (filter), stream_time);
330
331   if (!gst_video_frame_map (&in_frame, &filter->info, in, GST_MAP_READ))
332     goto invalid_in;
333
334   if (!gst_video_frame_map (&out_frame, &filter->info, out, GST_MAP_WRITE))
335     goto invalid_out;
336
337   src = GST_VIDEO_FRAME_PLANE_DATA (&in_frame, 0);
338   dest = GST_VIDEO_FRAME_PLANE_DATA (&out_frame, 0);
339
340   GST_OBJECT_LOCK (filter);
341   /* impact from the motion or rain drop */
342   if (filter->mode)
343     raindrop (filter);
344   else
345     motiondetect (filter, src);
346
347   /* simulate surface wave */
348   width = filter->map_w;
349   height = filter->map_h;
350
351   /* This function is called only 30 times per second. To increase a speed
352    * of wave, iterates this loop several times. */
353   for (i = loopnum; i > 0; i--) {
354     /* wave simulation */
355     p = filter->map1 + width + 1;
356     q = filter->map2 + width + 1;
357     r = filter->map3 + width + 1;
358     for (y = height - 2; y > 0; y--) {
359       for (x = width - 2; x > 0; x--) {
360         h = *(p - width - 1) + *(p - width + 1) + *(p + width - 1) + *(p +
361             width + 1)
362             + *(p - width) + *(p - 1) + *(p + 1) + *(p + width) - (*p) * 9;
363         h = h >> 3;
364         v = *p - *q;
365         v += h - (v >> decay);
366         *r = v + *p;
367         p++;
368         q++;
369         r++;
370       }
371       p += 2;
372       q += 2;
373       r += 2;
374     }
375
376     /* low pass filter */
377     p = filter->map3 + width + 1;
378     q = filter->map2 + width + 1;
379     for (y = height - 2; y > 0; y--) {
380       for (x = width - 2; x > 0; x--) {
381         h = *(p - width) + *(p - 1) + *(p + 1) + *(p + width) + (*p) * 60;
382         *q = h >> 6;
383         p++;
384         q++;
385       }
386       p += 2;
387       q += 2;
388     }
389
390     p = filter->map1;
391     filter->map1 = filter->map2;
392     filter->map2 = p;
393   }
394
395   vp = filter->vtable;
396   p = filter->map1;
397   for (y = height - 1; y > 0; y--) {
398     for (x = width - 1; x > 0; x--) {
399       /* difference of the height between two voxel. They are twiced to
400        * emphasise the wave. */
401       vp[0] = sqrtable[((p[0] - p[1]) >> (point - 1)) & 0xff];
402       vp[1] = sqrtable[((p[0] - p[width]) >> (point - 1)) & 0xff];
403       p++;
404       vp += 2;
405     }
406     p++;
407     vp += 2;
408   }
409
410   width = GST_VIDEO_FRAME_WIDTH (&in_frame);
411   height = GST_VIDEO_FRAME_HEIGHT (&in_frame);
412   vp = filter->vtable;
413
414   /* draw refracted image. The vector table is stretched. */
415   for (y = 0; y < height; y += 2) {
416     for (x = 0; x < width; x += 2) {
417       h = (gint) vp[0];
418       v = (gint) vp[1];
419       dx = x + h;
420       dy = y + v;
421       if (dx < 0)
422         dx = 0;
423       if (dy < 0)
424         dy = 0;
425       if (dx >= width)
426         dx = width - 1;
427       if (dy >= height)
428         dy = height - 1;
429       dest[0] = src[dy * width + dx];
430
431       i = dx;
432
433       dx = x + 1 + (h + (gint) vp[2]) / 2;
434       if (dx < 0)
435         dx = 0;
436       if (dx >= width)
437         dx = width - 1;
438       dest[1] = src[dy * width + dx];
439
440       dy = y + 1 + (v + (gint) vp[filter->map_w * 2 + 1]) / 2;
441       if (dy < 0)
442         dy = 0;
443       if (dy >= height)
444         dy = height - 1;
445       dest[width] = src[dy * width + i];
446
447       dest[width + 1] = src[dy * width + dx];
448       dest += 2;
449       vp += 2;
450     }
451     dest += width;
452     vp += 2;
453   }
454   GST_OBJECT_UNLOCK (filter);
455
456   gst_video_frame_unmap (&in_frame);
457   gst_video_frame_unmap (&out_frame);
458
459   return GST_FLOW_OK;
460
461   /* ERRORS */
462 invalid_in:
463   {
464     GST_DEBUG_OBJECT (filter, "invalid input frame");
465     return GST_FLOW_ERROR;
466   }
467 invalid_out:
468   {
469     GST_DEBUG_OBJECT (filter, "invalid output frame");
470     gst_video_frame_unmap (&in_frame);
471     return GST_FLOW_ERROR;
472   }
473
474 }
475
476 static gboolean
477 gst_rippletv_set_caps (GstBaseTransform * btrans, GstCaps * incaps,
478     GstCaps * outcaps)
479 {
480   GstRippleTV *filter = GST_RIPPLETV (btrans);
481   GstVideoInfo info;
482   gint width, height;
483
484   if (!gst_video_info_from_caps (&info, incaps))
485     goto invalid_caps;
486
487   filter->info = info;
488
489   width = GST_VIDEO_INFO_WIDTH (&info);
490   height = GST_VIDEO_INFO_HEIGHT (&info);
491
492   GST_OBJECT_LOCK (filter);
493   filter->map_h = height / 2 + 1;
494   filter->map_w = width / 2 + 1;
495
496   if (filter->map)
497     g_free (filter->map);
498   filter->map = g_new0 (gint, filter->map_h * filter->map_w * 3);
499
500   filter->map1 = filter->map;
501   filter->map2 = filter->map + filter->map_w * filter->map_h;
502   filter->map3 = filter->map + filter->map_w * filter->map_h * 2;
503
504   if (filter->vtable)
505     g_free (filter->vtable);
506   filter->vtable = g_new0 (gint8, filter->map_h * filter->map_w * 2);
507
508   if (filter->background)
509     g_free (filter->background);
510   filter->background = g_new0 (gint16, width * height);
511
512   if (filter->diff)
513     g_free (filter->diff);
514   filter->diff = g_new0 (guint8, width * height);
515   GST_OBJECT_UNLOCK (filter);
516
517   return TRUE;
518
519   /* ERRORS */
520 invalid_caps:
521   {
522     GST_DEBUG_OBJECT (filter, "invalid caps received");
523     return FALSE;
524   }
525 }
526
527 static gboolean
528 gst_rippletv_start (GstBaseTransform * trans)
529 {
530   GstRippleTV *filter = GST_RIPPLETV (trans);
531
532   filter->bg_is_set = FALSE;
533
534   filter->period = 0;
535   filter->rain_stat = 0;
536   filter->drop_prob = 0;
537   filter->drop_prob_increment = 0;
538   filter->drops_per_frame_max = 0;
539   filter->drops_per_frame = 0;
540   filter->drop_power = 0;
541
542   return TRUE;
543 }
544
545 static void
546 gst_rippletv_finalize (GObject * object)
547 {
548   GstRippleTV *filter = GST_RIPPLETV (object);
549
550   if (filter->map)
551     g_free (filter->map);
552   filter->map = NULL;
553
554   if (filter->vtable)
555     g_free (filter->vtable);
556   filter->vtable = NULL;
557
558   if (filter->background)
559     g_free (filter->background);
560   filter->background = NULL;
561
562   if (filter->diff)
563     g_free (filter->diff);
564   filter->diff = NULL;
565
566   G_OBJECT_CLASS (parent_class)->finalize (object);
567 }
568
569 static void
570 gst_rippletv_set_property (GObject * object, guint prop_id,
571     const GValue * value, GParamSpec * pspec)
572 {
573   GstRippleTV *filter = GST_RIPPLETV (object);
574
575   GST_OBJECT_LOCK (filter);
576   switch (prop_id) {
577     case PROP_RESET:{
578       memset (filter->map, 0,
579           filter->map_h * filter->map_w * 2 * sizeof (gint));
580       break;
581     }
582     case PROP_MODE:
583       filter->mode = g_value_get_enum (value);
584       break;
585     default:
586       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
587       break;
588   }
589   GST_OBJECT_UNLOCK (filter);
590 }
591
592 static void
593 gst_rippletv_get_property (GObject * object, guint prop_id, GValue * value,
594     GParamSpec * pspec)
595 {
596   GstRippleTV *filter = GST_RIPPLETV (object);
597
598   switch (prop_id) {
599     case PROP_MODE:
600       g_value_set_enum (value, filter->mode);
601       break;
602     default:
603       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
604       break;
605   }
606 }
607
608 static void
609 gst_rippletv_class_init (GstRippleTVClass * klass)
610 {
611   GObjectClass *gobject_class = (GObjectClass *) klass;
612   GstElementClass *gstelement_class = (GstElementClass *) klass;
613   GstBaseTransformClass *trans_class = (GstBaseTransformClass *) klass;
614
615   gobject_class->set_property = gst_rippletv_set_property;
616   gobject_class->get_property = gst_rippletv_get_property;
617
618   gobject_class->finalize = gst_rippletv_finalize;
619
620   g_object_class_install_property (gobject_class, PROP_RESET,
621       g_param_spec_boolean ("reset", "Reset",
622           "Reset all current ripples", FALSE,
623           G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS | GST_PARAM_CONTROLLABLE));
624
625   g_object_class_install_property (gobject_class, PROP_MODE,
626       g_param_spec_enum ("mode", "Mode",
627           "Mode", GST_TYPE_RIPPLETV_MODE, DEFAULT_MODE,
628           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | GST_PARAM_CONTROLLABLE));
629
630   gst_element_class_set_details_simple (gstelement_class, "RippleTV effect",
631       "Filter/Effect/Video",
632       "RippleTV does ripple mark effect on the video input",
633       "FUKUCHI, Kentarou <fukuchi@users.sourceforge.net>, "
634       "Sebastian Dröge <sebastian.droege@collabora.co.uk>");
635
636   gst_element_class_add_pad_template (gstelement_class,
637       gst_static_pad_template_get (&gst_rippletv_sink_template));
638   gst_element_class_add_pad_template (gstelement_class,
639       gst_static_pad_template_get (&gst_rippletv_src_template));
640
641   trans_class->set_caps = GST_DEBUG_FUNCPTR (gst_rippletv_set_caps);
642   trans_class->transform = GST_DEBUG_FUNCPTR (gst_rippletv_transform);
643   trans_class->start = GST_DEBUG_FUNCPTR (gst_rippletv_start);
644
645   setTable ();
646 }
647
648 static void
649 gst_rippletv_init (GstRippleTV * filter)
650 {
651   filter->mode = DEFAULT_MODE;
652
653   gst_pad_use_fixed_caps (GST_BASE_TRANSFORM_SRC_PAD (filter));
654   gst_pad_use_fixed_caps (GST_BASE_TRANSFORM_SINK_PAD (filter));
655 }