diff options
| author | Sebastian Dröge <sebastian.droege@collabora.co.uk> | 2009-07-15 14:37:19 +0200 | 
|---|---|---|
| committer | Sebastian Dröge <sebastian.droege@collabora.co.uk> | 2009-07-16 12:05:31 +0200 | 
| commit | 2abd58de9de6e4e367d50fca226b45ed9aaa6fe5 (patch) | |
| tree | eaccf5ded346162abeba3a88ab54ab821044f33d | |
| parent | 433255304e7fcfc4e9d434a44828280877a3a2f9 (diff) | |
effectv: Add rippletv element
This produces a water ripple effect on the video input,
based on motion or a rain drop algorithm.
Kindly relicensed to LGPL2+ by Kentaro Fukuchi <fukuchi@megaui.net>.
Fixes bug #588695.
| -rw-r--r-- | gst/effectv/Makefile.am | 4 | ||||
| -rw-r--r-- | gst/effectv/gsteffectv.c | 2 | ||||
| -rw-r--r-- | gst/effectv/gstripple.c | 610 | ||||
| -rw-r--r-- | gst/effectv/gstripple.h | 87 | 
4 files changed, 701 insertions, 2 deletions
| diff --git a/gst/effectv/Makefile.am b/gst/effectv/Makefile.am index 0aed7960..e0c28030 100644 --- a/gst/effectv/Makefile.am +++ b/gst/effectv/Makefile.am @@ -3,7 +3,7 @@ plugin_LTLIBRARIES = libgsteffectv.la  libgsteffectv_la_SOURCES = \  	gsteffectv.c gstedge.c gstaging.c gstdice.c gstwarp.c \  	gstshagadelic.c gstvertigo.c gstrev.c gstquark.c gstop.c \ -	gstradioac.c gststreak.c +	gstradioac.c gststreak.c gstripple.c  libgsteffectv_la_CFLAGS = \  	$(GST_PLUGINS_BASE_CFLAGS) \  	$(GST_CONTROLLER_CFLAGS) \ @@ -21,4 +21,4 @@ libgsteffectv_la_LIBTOOLFLAGS = --tag=disable-static  noinst_HEADERS = gsteffectv.h gstaging.h gstdice.h gstedge.h \          gstquark.h gstrev.h gstshagadelic.h gstvertigo.h gstwarp.h gstop.h \ -	gstradioac.h gststreak.h +	gstradioac.h gststreak.h gstripple.h diff --git a/gst/effectv/gsteffectv.c b/gst/effectv/gsteffectv.c index 57cbbca6..1615fd72 100644 --- a/gst/effectv/gsteffectv.c +++ b/gst/effectv/gsteffectv.c @@ -37,6 +37,7 @@  #include "gstop.h"  #include "gstradioac.h"  #include "gststreak.h" +#include "gstripple.h"  struct _elements_entry  { @@ -56,6 +57,7 @@ static const struct _elements_entry _elements[] = {    {"optv", gst_optv_get_type},    {"radioactv", gst_radioactv_get_type},    {"streaktv", gst_streaktv_get_type}, +  {"rippletv", gst_rippletv_get_type},    {NULL, 0},  }; diff --git a/gst/effectv/gstripple.c b/gst/effectv/gstripple.c new file mode 100644 index 00000000..ce4a58c9 --- /dev/null +++ b/gst/effectv/gstripple.c @@ -0,0 +1,610 @@ +/* GStreamer + * Copyright (C) <2009> Sebastian Dröge <sebastian.droege@collabora.co.uk> + * + * EffecTV - Realtime Digital Video Effector + * Copyright (C) 2001-2006 FUKUCHI Kentaro + * + * RippleTV - Water ripple effect. + * Copyright (C) 2001-2002 FUKUCHI Kentaro + * + * This combines the RippleTV and BaltanTV effects, which are + * very similar. BaltanTV is used if the feedback property is set + * to TRUE, otherwise RippleTV is used. + * + * EffecTV is free software. This library is free software; + * you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +/** + * SECTION:element-rippletv + * + * RippleTV does ripple mark effect on the video input. The ripple is caused + * by motion or random rain drops. + * + * <refsect2> + * <title>Example launch line</title> + * |[ + * gst-launch -v videotestsrc ! rippletv ! ffmpegcolorspace ! autovideosink + * ]| This pipeline shows the effect of rippletv on a test stream. + * </refsect2> + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <math.h> +#include <string.h> + +#include "gstripple.h" +#include "gsteffectv.h" + +#include <gst/video/video.h> + +#define DEFAULT_MODE 0 + +enum +{ +  PROP_0, +  PROP_RESET, +  PROP_MODE +}; + +static gint sqrtable[256]; + +#define GST_TYPE_RIPPLETV_MODE (gst_rippletv_mode_get_type()) +static GType +gst_rippletv_mode_get_type (void) +{ +  static GType type = 0; + +  static const GEnumValue enumvalue[] = { +    {0, "Motion Detection", "motion-detection"}, +    {1, "Rain", "rain"}, +    {0, NULL, NULL}, +  }; + +  if (!type) { +    type = g_enum_register_static ("GstRippleTVMode", enumvalue); +  } +  return type; +} + + +GST_BOILERPLATE (GstRippleTV, gst_rippletv, GstVideoFilter, +    GST_TYPE_VIDEO_FILTER); + +static GstStaticPadTemplate gst_rippletv_src_template = +    GST_STATIC_PAD_TEMPLATE ("src", +    GST_PAD_SRC, +    GST_PAD_ALWAYS, +    GST_STATIC_CAPS (GST_VIDEO_CAPS_BGRx "; " GST_VIDEO_CAPS_RGBx ";" +        GST_VIDEO_CAPS_xBGR "; " GST_VIDEO_CAPS_xRGB) +    ); + +static GstStaticPadTemplate gst_rippletv_sink_template = +    GST_STATIC_PAD_TEMPLATE ("sink", +    GST_PAD_SINK, +    GST_PAD_ALWAYS, +    GST_STATIC_CAPS (GST_VIDEO_CAPS_BGRx "; " GST_VIDEO_CAPS_RGBx ";" +        GST_VIDEO_CAPS_xBGR "; " GST_VIDEO_CAPS_xRGB) +    ); + +static const gint point = 16; +static const gint impact = 2; +static const gint decay = 8; +static const gint loopnum = 2; + +static void +setTable (void) +{ +  gint i; + +  for (i = 0; i < 128; i++) { +    sqrtable[i] = i * i; +  } +  for (i = 1; i <= 128; i++) { +    sqrtable[256 - i] = -i * i; +  } +} + +static void +image_bgset_y (guint32 * src, gint16 * background, gint video_area) +{ +  gint i; +  gint R, G, B; +  guint32 *p; +  gint16 *q; + +  p = src; +  q = background; +  for (i = 0; i < video_area; i++) { +    R = ((*p) & 0xff0000) >> (16 - 1); +    G = ((*p) & 0xff00) >> (8 - 2); +    B = (*p) & 0xff; +    *q = (gint16) (R + G + B); +    p++; +    q++; +  } +} + +static gint +setBackground (GstRippleTV * filter, guint32 * src) +{ +  image_bgset_y (src, filter->background, filter->width * filter->height); +  filter->bg_is_set = TRUE; + +  return 0; +} + +static void +image_bgsubtract_update_y (guint32 * src, gint16 * background, guint8 * diff, +    gint video_area) +{ +  gint i; +  gint R, G, B; +  guint32 *p; +  gint16 *q; +  guint8 *r; +  gint v; + +  p = src; +  q = background; +  r = diff; +  for (i = 0; i < video_area; i++) { +    R = ((*p) & 0xff0000) >> (16 - 1); +    G = ((*p) & 0xff00) >> (8 - 2); +    B = (*p) & 0xff; +    v = (R + G + B) - (gint) (*q); +    *q = (gint16) (R + G + B); +    *r = ((v + 70 * 7) >> 24) | ((70 * 7 - v) >> 24); + +    p++; +    q++; +    r++; +  } +} + +static void +motiondetect (GstRippleTV * filter, guint32 * src) +{ +  guint8 *diff = filter->diff; +  gint width = filter->width; +  gint *p, *q; +  gint x, y, h; + +  if (!filter->bg_is_set) +    setBackground (filter, src); + +  image_bgsubtract_update_y (src, filter->background, filter->diff, +      filter->width * filter->height); +  p = filter->map1 + filter->map_w + 1; +  q = filter->map2 + filter->map_w + 1; +  diff += filter->width + 2; + +  for (y = filter->map_h - 2; y > 0; y--) { +    for (x = filter->map_w - 2; x > 0; x--) { +      h = (gint) * diff + (gint) * (diff + 1) + (gint) * (diff + width) + +          (gint) * (diff + width + 1); +      if (h > 0) { +        *p = h << (point + impact - 8); +        *q = *p; +      } +      p++; +      q++; +      diff += 2; +    } +    diff += width + 2; +    p += 2; +    q += 2; +  } +} + +static inline void +drop (gint power, gint * map1, gint * map2, gint map_w, gint map_h) +{ +  gint x, y; +  gint *p, *q; + +  x = fastrand () % (map_w - 4) + 2; +  y = fastrand () % (map_h - 4) + 2; +  p = map1 + y * map_w + x; +  q = map2 + y * map_w + x; +  *p = power; +  *q = power; +  *(p - map_w) = *(p - 1) = *(p + 1) = *(p + map_w) = power / 2; +  *(p - map_w - 1) = *(p - map_w + 1) = *(p + map_w - 1) = *(p + map_w + 1) = +      power / 4; +  *(q - map_w) = *(q - 1) = *(q + 1) = *(q + map_w) = power / 2; +  *(q - map_w - 1) = *(q - map_w + 1) = *(q + map_w - 1) = *(p + map_w + 1) = +      power / 4; +} + +static void +raindrop (GstRippleTV * filter) +{ +  gint i; + +  if (filter->period == 0) { +    switch (filter->rain_stat) { +      case 0: +        filter->period = (fastrand () >> 23) + 100; +        filter->drop_prob = 0; +        filter->drop_prob_increment = 0x00ffffff / filter->period; +        filter->drop_power = (-(fastrand () >> 28) - 2) << point; +        filter->drops_per_frame_max = 2 << (fastrand () >> 30); // 2,4,8 or 16 +        filter->rain_stat = 1; +        break; +      case 1: +        filter->drop_prob = 0x00ffffff; +        filter->drops_per_frame = 1; +        filter->drop_prob_increment = 1; +        filter->period = (filter->drops_per_frame_max - 1) * 16; +        filter->rain_stat = 2; +        break; +      case 2: +        filter->period = (fastrand () >> 22) + 1000; +        filter->drop_prob_increment = 0; +        filter->rain_stat = 3; +        break; +      case 3: +        filter->period = (filter->drops_per_frame_max - 1) * 16; +        filter->drop_prob_increment = -1; +        filter->rain_stat = 4; +        break; +      case 4: +        filter->period = (fastrand () >> 24) + 60; +        filter->drop_prob_increment = -(filter->drop_prob / filter->period); +        filter->rain_stat = 5; +        break; +      case 5: +      default: +        filter->period = (fastrand () >> 23) + 500; +        filter->drop_prob = 0; +        filter->rain_stat = 0; +        break; +    } +  } +  switch (filter->rain_stat) { +    default: +    case 0: +      break; +    case 1: +    case 5: +      if ((fastrand () >> 8) < filter->drop_prob) { +        drop (filter->drop_power, filter->map1, filter->map2, filter->map_w, +            filter->map_h); +      } +      filter->drop_prob += filter->drop_prob_increment; +      break; +    case 2: +    case 3: +    case 4: +      for (i = filter->drops_per_frame / 16; i > 0; i--) { +        drop (filter->drop_power, filter->map1, filter->map2, filter->map_w, +            filter->map_h); +      } +      filter->drops_per_frame += filter->drop_prob_increment; +      break; +  } +  filter->period--; +} + +static GstFlowReturn +gst_rippletv_transform (GstBaseTransform * trans, GstBuffer * in, +    GstBuffer * out) +{ +  GstRippleTV *filter = GST_RIPPLETV (trans); +  guint32 *src, *dest; +  GstFlowReturn ret = GST_FLOW_OK; +  gint x, y, i; +  gint dx, dy; +  gint h, v; +  gint width, height; +  gint *p, *q, *r; +  gint8 *vp; + +  src = (guint32 *) GST_BUFFER_DATA (in); +  dest = (guint32 *) GST_BUFFER_DATA (out); + +  /* impact from the motion or rain drop */ +  if (filter->mode) +    raindrop (filter); +  else +    motiondetect (filter, src); + +  /* simulate surface wave */ +  width = filter->map_w; +  height = filter->map_h; + +  /* This function is called only 30 times per second. To increase a speed +   * of wave, iterates this loop several times. */ +  for (i = loopnum; i > 0; i--) { +    /* wave simulation */ +    p = filter->map1 + width + 1; +    q = filter->map2 + width + 1; +    r = filter->map3 + width + 1; +    for (y = height - 2; y > 0; y--) { +      for (x = width - 2; x > 0; x--) { +        h = *(p - width - 1) + *(p - width + 1) + *(p + width - 1) + *(p + +            width + 1) +            + *(p - width) + *(p - 1) + *(p + 1) + *(p + width) - (*p) * 9; +        h = h >> 3; +        v = *p - *q; +        v += h - (v >> decay); +        *r = v + *p; +        p++; +        q++; +        r++; +      } +      p += 2; +      q += 2; +      r += 2; +    } + +    /* low pass filter */ +    p = filter->map3 + width + 1; +    q = filter->map2 + width + 1; +    for (y = height - 2; y > 0; y--) { +      for (x = width - 2; x > 0; x--) { +        h = *(p - width) + *(p - 1) + *(p + 1) + *(p + width) + (*p) * 60; +        *q = h >> 6; +        p++; +        q++; +      } +      p += 2; +      q += 2; +    } + +    p = filter->map1; +    filter->map1 = filter->map2; +    filter->map2 = p; +  } + +  vp = filter->vtable; +  p = filter->map1; +  for (y = height - 1; y > 0; y--) { +    for (x = width - 1; x > 0; x--) { +      /* difference of the height between two voxel. They are twiced to +       * emphasise the wave. */ +      vp[0] = sqrtable[((p[0] - p[1]) >> (point - 1)) & 0xff]; +      vp[1] = sqrtable[((p[0] - p[width]) >> (point - 1)) & 0xff]; +      p++; +      vp += 2; +    } +    p++; +    vp += 2; +  } + +  height = filter->height; +  width = filter->width; +  vp = filter->vtable; + +  /* draw refracted image. The vector table is stretched. */ +  for (y = 0; y < height; y += 2) { +    for (x = 0; x < width; x += 2) { +      h = (gint) vp[0]; +      v = (gint) vp[1]; +      dx = x + h; +      dy = y + v; +      if (dx < 0) +        dx = 0; +      if (dy < 0) +        dy = 0; +      if (dx >= width) +        dx = width - 1; +      if (dy >= height) +        dy = height - 1; +      dest[0] = src[dy * width + dx]; + +      i = dx; + +      dx = x + 1 + (h + (gint) vp[2]) / 2; +      if (dx < 0) +        dx = 0; +      if (dx >= width) +        dx = width - 1; +      dest[1] = src[dy * width + dx]; + +      dy = y + 1 + (v + (gint) vp[filter->map_w * 2 + 1]) / 2; +      if (dy < 0) +        dy = 0; +      if (dy >= height) +        dy = height - 1; +      dest[width] = src[dy * width + i]; + +      dest[width + 1] = src[dy * width + dx]; +      dest += 2; +      vp += 2; +    } +    dest += filter->width; +    vp += 2; +  } + +  return ret; +} + +static gboolean +gst_rippletv_set_caps (GstBaseTransform * btrans, GstCaps * incaps, +    GstCaps * outcaps) +{ +  GstRippleTV *filter = GST_RIPPLETV (btrans); +  GstStructure *structure; +  gboolean ret = FALSE; + +  structure = gst_caps_get_structure (incaps, 0); + +  if (gst_structure_get_int (structure, "width", &filter->width) && +      gst_structure_get_int (structure, "height", &filter->height)) { + +    filter->map_h = filter->height / 2 + 1; +    filter->map_w = filter->width / 2 + 1; + +    if (filter->map) +      g_free (filter->map); +    filter->map = g_new0 (gint, filter->map_h * filter->map_w * 3); + +    filter->map1 = filter->map; +    filter->map2 = filter->map + filter->map_w * filter->map_h; +    filter->map3 = filter->map + filter->map_w * filter->map_h * 2; + +    if (filter->vtable) +      g_free (filter->vtable); +    filter->vtable = g_new0 (gint8, filter->map_h * filter->map_w * 2); + +    if (filter->background) +      g_free (filter->background); +    filter->background = g_new0 (gint16, filter->width * filter->height); + +    if (filter->diff) +      g_free (filter->diff); +    filter->diff = g_new0 (guint8, filter->width * filter->height); + +    ret = TRUE; +  } + +  return ret; +} + +static gboolean +gst_rippletv_start (GstBaseTransform * trans) +{ +  GstRippleTV *filter = GST_RIPPLETV (trans); + +  filter->bg_is_set = FALSE; + +  filter->period = 0; +  filter->rain_stat = 0; +  filter->drop_prob = 0; +  filter->drop_prob_increment = 0; +  filter->drops_per_frame_max = 0; +  filter->drops_per_frame = 0; +  filter->drop_power = 0; + +  return TRUE; +} + +static void +gst_rippletv_finalize (GObject * object) +{ +  GstRippleTV *filter = GST_RIPPLETV (object); + +  if (filter->map) +    g_free (filter->map); +  filter->map = NULL; + +  if (filter->vtable) +    g_free (filter->vtable); +  filter->vtable = NULL; + +  if (filter->background) +    g_free (filter->background); +  filter->background = NULL; + +  if (filter->diff) +    g_free (filter->diff); +  filter->diff = NULL; + +  G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static void +gst_rippletv_set_property (GObject * object, guint prop_id, +    const GValue * value, GParamSpec * pspec) +{ +  GstRippleTV *filter = GST_RIPPLETV (object); + +  switch (prop_id) { +    case PROP_RESET:{ +      memset (filter->map, 0, +          filter->map_h * filter->map_w * 2 * sizeof (gint)); +      break; +    } +    case PROP_MODE: +      filter->mode = g_value_get_enum (value); +      break; +    default: +      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); +      break; +  } +} + +static void +gst_rippletv_get_property (GObject * object, guint prop_id, GValue * value, +    GParamSpec * pspec) +{ +  GstRippleTV *filter = GST_RIPPLETV (object); + +  switch (prop_id) { +    case PROP_MODE: +      g_value_set_enum (value, filter->mode); +      break; +    default: +      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); +      break; +  } +} + +static void +gst_rippletv_base_init (gpointer g_class) +{ +  GstElementClass *element_class = GST_ELEMENT_CLASS (g_class); + +  gst_element_class_set_details_simple (element_class, "RippleTV effect", +      "Filter/Effect/Video", +      "RippleTV does ripple mark effect on the video input", +      "FUKUCHI, Kentarou <fukuchi@users.sourceforge.net>, " +      "Sebastian Dröge <sebastian.droege@collabora.co.uk>"); + +  gst_element_class_add_pad_template (element_class, +      gst_static_pad_template_get (&gst_rippletv_sink_template)); +  gst_element_class_add_pad_template (element_class, +      gst_static_pad_template_get (&gst_rippletv_src_template)); +} + +static void +gst_rippletv_class_init (GstRippleTVClass * klass) +{ +  GObjectClass *gobject_class = (GObjectClass *) klass; +  GstBaseTransformClass *trans_class = (GstBaseTransformClass *) klass; + +  gobject_class->set_property = gst_rippletv_set_property; +  gobject_class->get_property = gst_rippletv_get_property; + +  gobject_class->finalize = gst_rippletv_finalize; + +  g_object_class_install_property (gobject_class, PROP_RESET, +      g_param_spec_boolean ("reset", "Reset", +          "Reset all current ripples", FALSE, +          G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS)); + +  g_object_class_install_property (gobject_class, PROP_MODE, +      g_param_spec_enum ("mode", "Mode", +          "Mode", GST_TYPE_RIPPLETV_MODE, DEFAULT_MODE, +          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + +  trans_class->set_caps = GST_DEBUG_FUNCPTR (gst_rippletv_set_caps); +  trans_class->transform = GST_DEBUG_FUNCPTR (gst_rippletv_transform); +  trans_class->start = GST_DEBUG_FUNCPTR (gst_rippletv_start); + +  setTable (); +} + +static void +gst_rippletv_init (GstRippleTV * filter, GstRippleTVClass * klass) +{ +  filter->mode = DEFAULT_MODE; +} diff --git a/gst/effectv/gstripple.h b/gst/effectv/gstripple.h new file mode 100644 index 00000000..388ba566 --- /dev/null +++ b/gst/effectv/gstripple.h @@ -0,0 +1,87 @@ +/* GStreamer + * Copyright (C) <2009> Sebastian Dröge <sebastian.droege@collabora.co.uk> + * + * EffecTV - Realtime Digital Video Effector + * Copyright (C) 2001-2006 FUKUCHI Kentaro + * + * RippleTV - Water ripple effect. + * Copyright (C) 2001-2002 FUKUCHI Kentaro + * + * EffecTV is free software. This library is free software; + * you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GST_RIPPLE_H__ +#define __GST_RIPPLE_H__ + +#include <gst/gst.h> + +#include <gst/video/gstvideofilter.h> + +G_BEGIN_DECLS + +#define GST_TYPE_RIPPLETV \ +  (gst_rippletv_get_type()) +#define GST_RIPPLETV(obj) \ +  (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_RIPPLETV,GstRippleTV)) +#define GST_RIPPLETV_CLASS(klass) \ +  (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_RIPPLETV,GstRippleTVClass)) +#define GST_IS_RIPPLETV(obj) \ +  (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_RIPPLETV)) +#define GST_IS_RIPPLETV_CLASS(klass) \ +  (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_RIPPLETV)) + +typedef struct _GstRippleTV GstRippleTV; +typedef struct _GstRippleTVClass GstRippleTVClass; + +struct _GstRippleTV +{ +  GstVideoFilter element; + +  /* < private > */ +  gint width, height; + +  gint mode; + +  gint16 *background; +  guint8 *diff; + +  gint *map, *map1, *map2, *map3; +  gint map_h, map_w; + +  gint8 *vtable; + +  gboolean bg_is_set; + +  gint period; +  gint rain_stat; +  guint drop_prob; +  gint drop_prob_increment; +  gint drops_per_frame_max; +  gint drops_per_frame; +  gint drop_power; +}; + +struct _GstRippleTVClass +{ +  GstVideoFilterClass parent_class; +}; + +GType gst_rippletv_get_type (void); + +G_END_DECLS + +#endif /* __GST_RIPPLE_H__ */ | 
