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