tizen 2.0 init
[framework/multimedia/gst-plugins-good0.10.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 ! ffmpegcolorspace ! 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 #include <gst/video/video.h>
56 #include <gst/controller/gstcontroller.h>
57
58 #define DEFAULT_MODE 0
59
60 enum
61 {
62   PROP_0,
63   PROP_RESET,
64   PROP_MODE
65 };
66
67 static gint sqrtable[256];
68
69 #define GST_TYPE_RIPPLETV_MODE (gst_rippletv_mode_get_type())
70 static GType
71 gst_rippletv_mode_get_type (void)
72 {
73   static GType type = 0;
74
75   static const GEnumValue enumvalue[] = {
76     {0, "Motion Detection", "motion-detection"},
77     {1, "Rain", "rain"},
78     {0, NULL, NULL},
79   };
80
81   if (!type) {
82     type = g_enum_register_static ("GstRippleTVMode", enumvalue);
83   }
84   return type;
85 }
86
87
88 GST_BOILERPLATE (GstRippleTV, gst_rippletv, GstVideoFilter,
89     GST_TYPE_VIDEO_FILTER);
90
91 static GstStaticPadTemplate gst_rippletv_src_template =
92     GST_STATIC_PAD_TEMPLATE ("src",
93     GST_PAD_SRC,
94     GST_PAD_ALWAYS,
95     GST_STATIC_CAPS (GST_VIDEO_CAPS_BGRx "; " GST_VIDEO_CAPS_RGBx ";"
96         GST_VIDEO_CAPS_xBGR "; " GST_VIDEO_CAPS_xRGB)
97     );
98
99 static GstStaticPadTemplate gst_rippletv_sink_template =
100     GST_STATIC_PAD_TEMPLATE ("sink",
101     GST_PAD_SINK,
102     GST_PAD_ALWAYS,
103     GST_STATIC_CAPS (GST_VIDEO_CAPS_BGRx "; " GST_VIDEO_CAPS_RGBx ";"
104         GST_VIDEO_CAPS_xBGR "; " GST_VIDEO_CAPS_xRGB)
105     );
106
107 static const gint point = 16;
108 static const gint impact = 2;
109 static const gint decay = 8;
110 static const gint loopnum = 2;
111
112 static void
113 setTable (void)
114 {
115   gint i;
116
117   for (i = 0; i < 128; i++) {
118     sqrtable[i] = i * i;
119   }
120   for (i = 1; i <= 128; i++) {
121     sqrtable[256 - i] = -i * i;
122   }
123 }
124
125 static void
126 image_bgset_y (guint32 * src, gint16 * background, gint video_area)
127 {
128   gint i;
129   gint R, G, B;
130   guint32 *p;
131   gint16 *q;
132
133   p = src;
134   q = background;
135   for (i = 0; i < video_area; i++) {
136     R = ((*p) & 0xff0000) >> (16 - 1);
137     G = ((*p) & 0xff00) >> (8 - 2);
138     B = (*p) & 0xff;
139     *q = (gint16) (R + G + B);
140     p++;
141     q++;
142   }
143 }
144
145 static gint
146 setBackground (GstRippleTV * filter, guint32 * src)
147 {
148   image_bgset_y (src, filter->background, filter->width * filter->height);
149   filter->bg_is_set = TRUE;
150
151   return 0;
152 }
153
154 static void
155 image_bgsubtract_update_y (guint32 * src, gint16 * background, guint8 * diff,
156     gint video_area)
157 {
158   gint i;
159   gint R, G, B;
160   guint32 *p;
161   gint16 *q;
162   guint8 *r;
163   gint v;
164
165   p = src;
166   q = background;
167   r = diff;
168   for (i = 0; i < video_area; i++) {
169     R = ((*p) & 0xff0000) >> (16 - 1);
170     G = ((*p) & 0xff00) >> (8 - 2);
171     B = (*p) & 0xff;
172     v = (R + G + B) - (gint) (*q);
173     *q = (gint16) (R + G + B);
174     *r = ((v + 70 * 7) >> 24) | ((70 * 7 - v) >> 24);
175
176     p++;
177     q++;
178     r++;
179   }
180 }
181
182 static void
183 motiondetect (GstRippleTV * filter, guint32 * src)
184 {
185   guint8 *diff = filter->diff;
186   gint width = filter->width;
187   gint *p, *q;
188   gint x, y, h;
189
190   if (!filter->bg_is_set)
191     setBackground (filter, src);
192
193   image_bgsubtract_update_y (src, filter->background, filter->diff,
194       filter->width * filter->height);
195   p = filter->map1 + filter->map_w + 1;
196   q = filter->map2 + filter->map_w + 1;
197   diff += filter->width + 2;
198
199   for (y = filter->map_h - 2; y > 0; y--) {
200     for (x = filter->map_w - 2; x > 0; x--) {
201       h = (gint) * diff + (gint) * (diff + 1) + (gint) * (diff + width) +
202           (gint) * (diff + width + 1);
203       if (h > 0) {
204         *p = h << (point + impact - 8);
205         *q = *p;
206       }
207       p++;
208       q++;
209       diff += 2;
210     }
211     diff += width + 2;
212     p += 2;
213     q += 2;
214   }
215 }
216
217 static inline void
218 drop (gint power, gint * map1, gint * map2, gint map_w, gint map_h)
219 {
220   gint x, y;
221   gint *p, *q;
222
223   x = fastrand () % (map_w - 4) + 2;
224   y = fastrand () % (map_h - 4) + 2;
225   p = map1 + y * map_w + x;
226   q = map2 + y * map_w + x;
227   *p = power;
228   *q = power;
229   *(p - map_w) = *(p - 1) = *(p + 1) = *(p + map_w) = power / 2;
230   *(p - map_w - 1) = *(p - map_w + 1) = *(p + map_w - 1) = *(p + map_w + 1) =
231       power / 4;
232   *(q - map_w) = *(q - 1) = *(q + 1) = *(q + map_w) = power / 2;
233   *(q - map_w - 1) = *(q - map_w + 1) = *(q + map_w - 1) = *(p + map_w + 1) =
234       power / 4;
235 }
236
237 static void
238 raindrop (GstRippleTV * filter)
239 {
240   gint i;
241
242   if (filter->period == 0) {
243     switch (filter->rain_stat) {
244       case 0:
245         filter->period = (fastrand () >> 23) + 100;
246         filter->drop_prob = 0;
247         filter->drop_prob_increment = 0x00ffffff / filter->period;
248         filter->drop_power = (-(fastrand () >> 28) - 2) << point;
249         filter->drops_per_frame_max = 2 << (fastrand () >> 30); // 2,4,8 or 16
250         filter->rain_stat = 1;
251         break;
252       case 1:
253         filter->drop_prob = 0x00ffffff;
254         filter->drops_per_frame = 1;
255         filter->drop_prob_increment = 1;
256         filter->period = (filter->drops_per_frame_max - 1) * 16;
257         filter->rain_stat = 2;
258         break;
259       case 2:
260         filter->period = (fastrand () >> 22) + 1000;
261         filter->drop_prob_increment = 0;
262         filter->rain_stat = 3;
263         break;
264       case 3:
265         filter->period = (filter->drops_per_frame_max - 1) * 16;
266         filter->drop_prob_increment = -1;
267         filter->rain_stat = 4;
268         break;
269       case 4:
270         filter->period = (fastrand () >> 24) + 60;
271         filter->drop_prob_increment = -(filter->drop_prob / filter->period);
272         filter->rain_stat = 5;
273         break;
274       case 5:
275       default:
276         filter->period = (fastrand () >> 23) + 500;
277         filter->drop_prob = 0;
278         filter->rain_stat = 0;
279         break;
280     }
281   }
282   switch (filter->rain_stat) {
283     default:
284     case 0:
285       break;
286     case 1:
287     case 5:
288       if ((fastrand () >> 8) < filter->drop_prob) {
289         drop (filter->drop_power, filter->map1, filter->map2, filter->map_w,
290             filter->map_h);
291       }
292       filter->drop_prob += filter->drop_prob_increment;
293       break;
294     case 2:
295     case 3:
296     case 4:
297       for (i = filter->drops_per_frame / 16; i > 0; i--) {
298         drop (filter->drop_power, filter->map1, filter->map2, filter->map_w,
299             filter->map_h);
300       }
301       filter->drops_per_frame += filter->drop_prob_increment;
302       break;
303   }
304   filter->period--;
305 }
306
307 static GstFlowReturn
308 gst_rippletv_transform (GstBaseTransform * trans, GstBuffer * in,
309     GstBuffer * out)
310 {
311   GstRippleTV *filter = GST_RIPPLETV (trans);
312   guint32 *src, *dest;
313   GstFlowReturn ret = GST_FLOW_OK;
314   gint x, y, i;
315   gint dx, dy, o_dx;
316   gint h, v;
317   gint m_w, m_h, v_w, v_h;
318   gint *p, *q, *r;
319   gint8 *vp;
320   GstClockTime timestamp, stream_time;
321
322   timestamp = GST_BUFFER_TIMESTAMP (in);
323   stream_time =
324       gst_segment_to_stream_time (&trans->segment, GST_FORMAT_TIME, timestamp);
325
326   GST_DEBUG_OBJECT (filter, "sync to %" GST_TIME_FORMAT,
327       GST_TIME_ARGS (timestamp));
328
329   if (GST_CLOCK_TIME_IS_VALID (stream_time))
330     gst_object_sync_values (G_OBJECT (filter), stream_time);
331
332   src = (guint32 *) GST_BUFFER_DATA (in);
333   dest = (guint32 *) GST_BUFFER_DATA (out);
334
335   GST_OBJECT_LOCK (filter);
336   /* impact from the motion or rain drop */
337   if (filter->mode)
338     raindrop (filter);
339   else
340     motiondetect (filter, src);
341
342   m_w = filter->map_w;
343   m_h = filter->map_h;
344   v_w = filter->width;
345   v_h = filter->height;
346
347   /* simulate surface wave */
348
349   /* This function is called only 30 times per second. To increase a speed
350    * of wave, iterates this loop several times. */
351   for (i = loopnum; i > 0; i--) {
352     /* wave simulation */
353     p = filter->map1 + m_w + 1;
354     q = filter->map2 + m_w + 1;
355     r = filter->map3 + m_w + 1;
356     for (y = m_h - 2; y > 0; y--) {
357       for (x = m_w - 2; x > 0; x--) {
358         h = *(p - m_w - 1) + *(p - m_w + 1) + *(p + m_w - 1) + *(p + m_w + 1)
359             + *(p - m_w) + *(p - 1) + *(p + 1) + *(p + m_w) - (*p) * 9;
360         h = h >> 3;
361         v = *p - *q;
362         v += h - (v >> decay);
363         *r = v + *p;
364         p++;
365         q++;
366         r++;
367       }
368       p += 2;
369       q += 2;
370       r += 2;
371     }
372
373     /* low pass filter */
374     p = filter->map3 + m_w + 1;
375     q = filter->map2 + m_w + 1;
376     for (y = m_h - 2; y > 0; y--) {
377       for (x = m_w - 2; x > 0; x--) {
378         h = *(p - m_w) + *(p - 1) + *(p + 1) + *(p + m_w) + (*p) * 60;
379         *q = h >> 6;
380         p++;
381         q++;
382       }
383       p += 2;
384       q += 2;
385     }
386
387     p = filter->map1;
388     filter->map1 = filter->map2;
389     filter->map2 = p;
390   }
391
392   vp = filter->vtable;
393   p = filter->map1;
394   for (y = m_h - 1; y > 0; y--) {
395     for (x = m_w - 1; x > 0; x--) {
396       /* difference of the height between two voxel. They are twiced to
397        * emphasise the wave. */
398       vp[0] = sqrtable[((p[0] - p[1]) >> (point - 1)) & 0xff];
399       vp[1] = sqrtable[((p[0] - p[m_w]) >> (point - 1)) & 0xff];
400       p++;
401       vp += 2;
402     }
403     p++;
404     vp += 2;
405   }
406
407   vp = filter->vtable;
408
409   /* draw refracted image. The vector table is stretched. */
410   for (y = 0; y < v_h; y += 2) {
411     for (x = 0; x < v_w; x += 2) {
412       h = (gint) vp[0];
413       v = (gint) vp[1];
414       dx = x + h;
415       dy = y + v;
416       dx = CLAMP (dx, 0, (v_w - 2));
417       dy = CLAMP (dy, 0, (v_h - 2));
418       dest[0] = src[dy * v_w + dx];
419
420       o_dx = dx;
421
422       dx = x + 1 + (h + (gint) vp[2]) / 2;
423       dx = CLAMP (dx, 0, (v_w - 2));
424       dest[1] = src[dy * v_w + dx];
425
426       dy = y + 1 + (v + (gint) vp[m_w * 2 + 1]) / 2;
427       dy = CLAMP (dy, 0, (v_h - 2));
428       dest[v_w] = src[dy * v_w + o_dx];
429
430       dest[v_w + 1] = src[dy * v_w + dx];
431       dest += 2;
432       vp += 2;
433     }
434     dest += v_w;
435     vp += 2;
436   }
437   GST_OBJECT_UNLOCK (filter);
438
439   return ret;
440 }
441
442 static gboolean
443 gst_rippletv_set_caps (GstBaseTransform * btrans, GstCaps * incaps,
444     GstCaps * outcaps)
445 {
446   GstRippleTV *filter = GST_RIPPLETV (btrans);
447   GstStructure *structure;
448   gboolean ret = FALSE;
449
450   structure = gst_caps_get_structure (incaps, 0);
451
452   GST_OBJECT_LOCK (filter);
453   if (gst_structure_get_int (structure, "width", &filter->width) &&
454       gst_structure_get_int (structure, "height", &filter->height)) {
455
456     filter->map_h = filter->height / 2 + 1;
457     filter->map_w = filter->width / 2 + 1;
458
459     /* we over allocate the buffers, as the render code does not handle clipping
460      * very well */
461     if (filter->map)
462       g_free (filter->map);
463     filter->map = g_new0 (gint, (1 + filter->map_h) * filter->map_w * 3);
464
465     filter->map1 = filter->map;
466     filter->map2 = filter->map + filter->map_w * filter->map_h;
467     filter->map3 = filter->map + filter->map_w * filter->map_h * 2;
468
469     if (filter->vtable)
470       g_free (filter->vtable);
471     filter->vtable = g_new0 (gint8, (1 + filter->map_h) * filter->map_w * 2);
472
473     if (filter->background)
474       g_free (filter->background);
475     filter->background = g_new0 (gint16, filter->width * (filter->height + 1));
476
477     if (filter->diff)
478       g_free (filter->diff);
479     filter->diff = g_new0 (guint8, filter->width * (filter->height + 1));
480
481     ret = TRUE;
482   }
483   GST_OBJECT_UNLOCK (filter);
484
485   return ret;
486 }
487
488 static gboolean
489 gst_rippletv_start (GstBaseTransform * trans)
490 {
491   GstRippleTV *filter = GST_RIPPLETV (trans);
492
493   filter->bg_is_set = FALSE;
494
495   filter->period = 0;
496   filter->rain_stat = 0;
497   filter->drop_prob = 0;
498   filter->drop_prob_increment = 0;
499   filter->drops_per_frame_max = 0;
500   filter->drops_per_frame = 0;
501   filter->drop_power = 0;
502
503   return TRUE;
504 }
505
506 static void
507 gst_rippletv_finalize (GObject * object)
508 {
509   GstRippleTV *filter = GST_RIPPLETV (object);
510
511   if (filter->map)
512     g_free (filter->map);
513   filter->map = NULL;
514
515   if (filter->vtable)
516     g_free (filter->vtable);
517   filter->vtable = NULL;
518
519   if (filter->background)
520     g_free (filter->background);
521   filter->background = NULL;
522
523   if (filter->diff)
524     g_free (filter->diff);
525   filter->diff = NULL;
526
527   G_OBJECT_CLASS (parent_class)->finalize (object);
528 }
529
530 static void
531 gst_rippletv_set_property (GObject * object, guint prop_id,
532     const GValue * value, GParamSpec * pspec)
533 {
534   GstRippleTV *filter = GST_RIPPLETV (object);
535
536   GST_OBJECT_LOCK (filter);
537   switch (prop_id) {
538     case PROP_RESET:{
539       memset (filter->map, 0,
540           filter->map_h * filter->map_w * 2 * sizeof (gint));
541       break;
542     }
543     case PROP_MODE:
544       filter->mode = g_value_get_enum (value);
545       break;
546     default:
547       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
548       break;
549   }
550   GST_OBJECT_UNLOCK (filter);
551 }
552
553 static void
554 gst_rippletv_get_property (GObject * object, guint prop_id, GValue * value,
555     GParamSpec * pspec)
556 {
557   GstRippleTV *filter = GST_RIPPLETV (object);
558
559   switch (prop_id) {
560     case PROP_MODE:
561       g_value_set_enum (value, filter->mode);
562       break;
563     default:
564       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
565       break;
566   }
567 }
568
569 static void
570 gst_rippletv_base_init (gpointer g_class)
571 {
572   GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
573
574   gst_element_class_set_details_simple (element_class, "RippleTV effect",
575       "Filter/Effect/Video",
576       "RippleTV does ripple mark effect on the video input",
577       "FUKUCHI, Kentarou <fukuchi@users.sourceforge.net>, "
578       "Sebastian Dröge <sebastian.droege@collabora.co.uk>");
579
580   gst_element_class_add_static_pad_template (element_class,
581       &gst_rippletv_sink_template);
582   gst_element_class_add_static_pad_template (element_class,
583       &gst_rippletv_src_template);
584 }
585
586 static void
587 gst_rippletv_class_init (GstRippleTVClass * klass)
588 {
589   GObjectClass *gobject_class = (GObjectClass *) klass;
590   GstBaseTransformClass *trans_class = (GstBaseTransformClass *) klass;
591
592   gobject_class->set_property = gst_rippletv_set_property;
593   gobject_class->get_property = gst_rippletv_get_property;
594
595   gobject_class->finalize = gst_rippletv_finalize;
596
597   g_object_class_install_property (gobject_class, PROP_RESET,
598       g_param_spec_boolean ("reset", "Reset",
599           "Reset all current ripples", FALSE,
600           G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS | GST_PARAM_CONTROLLABLE));
601
602   g_object_class_install_property (gobject_class, PROP_MODE,
603       g_param_spec_enum ("mode", "Mode",
604           "Mode", GST_TYPE_RIPPLETV_MODE, DEFAULT_MODE,
605           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | GST_PARAM_CONTROLLABLE));
606
607   trans_class->set_caps = GST_DEBUG_FUNCPTR (gst_rippletv_set_caps);
608   trans_class->transform = GST_DEBUG_FUNCPTR (gst_rippletv_transform);
609   trans_class->start = GST_DEBUG_FUNCPTR (gst_rippletv_start);
610
611   setTable ();
612 }
613
614 static void
615 gst_rippletv_init (GstRippleTV * filter, GstRippleTVClass * klass)
616 {
617   filter->mode = DEFAULT_MODE;
618
619   /* FIXME: remove this when memory corruption after resizes are fixed */
620   gst_pad_use_fixed_caps (GST_BASE_TRANSFORM_SRC_PAD (filter));
621   gst_pad_use_fixed_caps (GST_BASE_TRANSFORM_SINK_PAD (filter));
622 }