yadif: Add YADIF deinterlacing filter
authorDavid Schleef <ds@schleef.org>
Thu, 7 Feb 2013 23:09:51 +0000 (15:09 -0800)
committerDavid Schleef <ds@schleef.org>
Mon, 11 Feb 2013 20:30:29 +0000 (12:30 -0800)
Code copied from Libav, commit 26e4f0c70.  Will eventually be
moved to -ugly because underlying code is GPL.

configure.ac
gst/yadif/Makefile.am [new file with mode: 0644]
gst/yadif/gstyadif.c [new file with mode: 0644]
gst/yadif/gstyadif.h [new file with mode: 0644]
gst/yadif/vf_yadif.c [new file with mode: 0644]
gst/yadif/yadif.c [new file with mode: 0644]
gst/yadif/yadif_template.c [new file with mode: 0644]

index e8d4028..9f73879 100644 (file)
@@ -396,6 +396,7 @@ AG_GST_CHECK_PLUGIN(videoparsers)
 AG_GST_CHECK_PLUGIN(videosignal)
 AG_GST_CHECK_PLUGIN(vmnc)
 AG_GST_CHECK_PLUGIN(y4m)
+AG_GST_CHECK_PLUGIN(yadif)
 
 dnl *** plug-ins to exclude ***
 
@@ -2248,6 +2249,7 @@ gst/videoparsers/Makefile
 gst/videosignal/Makefile
 gst/vmnc/Makefile
 gst/y4m/Makefile
+gst/yadif/Makefile
 gst-libs/Makefile
 gst-libs/gst/Makefile
 gst-libs/gst/basecamerabinsrc/Makefile
diff --git a/gst/yadif/Makefile.am b/gst/yadif/Makefile.am
new file mode 100644 (file)
index 0000000..c01afc7
--- /dev/null
@@ -0,0 +1,22 @@
+plugin_LTLIBRARIES = libgstyadif.la
+
+libgstyadif_la_SOURCES = gstyadif.c gstyadif.h vf_yadif.c yadif.c
+libgstyadif_la_CFLAGS = $(GST_BASE_CFLAGS) $(GST_CFLAGS)
+libgstyadif_la_LIBADD = -lgstvideo-1.0 $(GST_BASE_LIBS) $(GST_LIBS)
+libgstyadif_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
+libgstyadif_la_LIBTOOLFLAGS = --tag=disable-static
+
+
+Android.mk: Makefile.am $(BUILT_SOURCES)
+       androgenizer \
+       -:PROJECT libgstyadif -:SHARED libgstyadif \
+        -:TAGS eng debug \
+         -:REL_TOP $(top_srcdir) -:ABS_TOP $(abs_top_srcdir) \
+        -:SOURCES $(libgstyadif_la_SOURCES) \
+        -:CFLAGS $(DEFS) $(DEFAULT_INCLUDES) $(libgstyadif_la_CFLAGS) \
+        -:LDFLAGS $(libgstyadif_la_LDFLAGS) \
+                  $(libgstyadif_la_LIBADD) \
+                  -ldl \
+        -:PASSTHROUGH LOCAL_ARM_MODE:=arm \
+                      LOCAL_MODULE_PATH:='$$(TARGET_OUT)/lib/gstreamer-0.10' \
+       > $@
diff --git a/gst/yadif/gstyadif.c b/gst/yadif/gstyadif.c
new file mode 100644 (file)
index 0000000..cde9184
--- /dev/null
@@ -0,0 +1,493 @@
+/* GStreamer
+ * Copyright (C) 2013 David Schleef <ds@schleef.org>
+ * Copyright (C) 2013 Rdio, Inc. <ingestions@rd.io>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU 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 General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Suite 500,
+ * Boston, MA 02110-1335, USA.
+ */
+/**
+ * SECTION:element-gstyadif
+ *
+ * The yadif element deinterlaces video, using the YADIF deinterlacing
+ * filter copied from Libav.  This element only handles the simple case
+ * of interlaced-mode=interleaved video instead of the more complex
+ * inverse telecine and deinterlace cases that are handled by the
+ * deinterlace element.
+ *
+ * <refsect2>
+ * <title>Example launch line</title>
+ * |[
+ * gst-launch -v videotestsrc pattern=ball ! interlace ! yadif ! xvimagesink
+ * ]|
+ * This pipeline creates an interlaced test pattern, and then deinterlaces
+ * it using the yadif filter.
+ * </refsect2>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <gst/gst.h>
+#include <gst/base/gstbasetransform.h>
+#include <gst/video/video.h>
+#include "gstyadif.h"
+
+GST_DEBUG_CATEGORY_STATIC (gst_yadif_debug_category);
+#define GST_CAT_DEFAULT gst_yadif_debug_category
+
+/* prototypes */
+
+
+static void gst_yadif_set_property (GObject * object,
+    guint property_id, const GValue * value, GParamSpec * pspec);
+static void gst_yadif_get_property (GObject * object,
+    guint property_id, GValue * value, GParamSpec * pspec);
+static void gst_yadif_dispose (GObject * object);
+static void gst_yadif_finalize (GObject * object);
+
+static GstCaps *gst_yadif_transform_caps (GstBaseTransform * trans,
+    GstPadDirection direction, GstCaps * caps, GstCaps * filter);
+static GstCaps *gst_yadif_fixate_caps (GstBaseTransform * trans,
+    GstPadDirection direction, GstCaps * caps, GstCaps * othercaps);
+static gboolean gst_yadif_accept_caps (GstBaseTransform * trans,
+    GstPadDirection direction, GstCaps * caps);
+static gboolean gst_yadif_set_caps (GstBaseTransform * trans, GstCaps * incaps,
+    GstCaps * outcaps);
+static gboolean gst_yadif_query (GstBaseTransform * trans,
+    GstPadDirection direction, GstQuery * query);
+static gboolean gst_yadif_decide_allocation (GstBaseTransform * trans,
+    GstQuery * query);
+static gboolean gst_yadif_filter_meta (GstBaseTransform * trans,
+    GstQuery * query, GType api, const GstStructure * params);
+static gboolean gst_yadif_propose_allocation (GstBaseTransform * trans,
+    GstQuery * decide_query, GstQuery * query);
+static gboolean gst_yadif_transform_size (GstBaseTransform * trans,
+    GstPadDirection direction, GstCaps * caps, gsize size, GstCaps * othercaps,
+    gsize * othersize);
+static gboolean gst_yadif_get_unit_size (GstBaseTransform * trans,
+    GstCaps * caps, gsize * size);
+static gboolean gst_yadif_start (GstBaseTransform * trans);
+static gboolean gst_yadif_stop (GstBaseTransform * trans);
+static gboolean gst_yadif_sink_event (GstBaseTransform * trans,
+    GstEvent * event);
+static gboolean gst_yadif_src_event (GstBaseTransform * trans,
+    GstEvent * event);
+static GstFlowReturn gst_yadif_prepare_output_buffer (GstBaseTransform * trans,
+    GstBuffer * input, GstBuffer ** outbuf);
+static gboolean gst_yadif_copy_metadata (GstBaseTransform * trans,
+    GstBuffer * input, GstBuffer * outbuf);
+static gboolean gst_yadif_transform_meta (GstBaseTransform * trans,
+    GstBuffer * outbuf, GstMeta * meta, GstBuffer * inbuf);
+static void gst_yadif_before_transform (GstBaseTransform * trans,
+    GstBuffer * buffer);
+static GstFlowReturn gst_yadif_transform (GstBaseTransform * trans,
+    GstBuffer * inbuf, GstBuffer * outbuf);
+static GstFlowReturn gst_yadif_transform_ip (GstBaseTransform * trans,
+    GstBuffer * buf);
+
+enum
+{
+  PROP_0
+};
+
+/* pad templates */
+
+static GstStaticPadTemplate gst_yadif_sink_template =
+GST_STATIC_PAD_TEMPLATE ("sink",
+    GST_PAD_SINK,
+    GST_PAD_ALWAYS,
+    GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE ("{Y42B,I420,Y444}")
+        ",interlace-mode=(string){interleaved,mixed,progressive}")
+    );
+
+static GstStaticPadTemplate gst_yadif_src_template =
+GST_STATIC_PAD_TEMPLATE ("src",
+    GST_PAD_SRC,
+    GST_PAD_ALWAYS,
+    GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE ("{Y42B,I420,Y444}")
+        ",interlace-mode=(string)progressive")
+    );
+
+
+/* class initialization */
+
+G_DEFINE_TYPE_WITH_CODE (GstYadif, gst_yadif, GST_TYPE_BASE_TRANSFORM,
+    GST_DEBUG_CATEGORY_INIT (gst_yadif_debug_category, "yadif", 0,
+        "debug category for yadif element"));
+
+static void
+gst_yadif_class_init (GstYadifClass * klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+  GstBaseTransformClass *base_transform_class =
+      GST_BASE_TRANSFORM_CLASS (klass);
+
+  /* Setting up pads and setting metadata should be moved to
+     base_class_init if you intend to subclass this class. */
+  gst_element_class_add_pad_template (GST_ELEMENT_CLASS (klass),
+      gst_static_pad_template_get (&gst_yadif_sink_template));
+  gst_element_class_add_pad_template (GST_ELEMENT_CLASS (klass),
+      gst_static_pad_template_get (&gst_yadif_src_template));
+
+  gst_element_class_set_static_metadata (GST_ELEMENT_CLASS (klass),
+      "YADIF deinterlacer", "Video/Filter",
+      "Deinterlace video using YADIF filter", "David Schleef <ds@schleef.org>");
+
+  gobject_class->set_property = gst_yadif_set_property;
+  gobject_class->get_property = gst_yadif_get_property;
+  gobject_class->dispose = gst_yadif_dispose;
+  gobject_class->finalize = gst_yadif_finalize;
+  base_transform_class->transform_caps =
+      GST_DEBUG_FUNCPTR (gst_yadif_transform_caps);
+  if (0)
+    base_transform_class->fixate_caps =
+        GST_DEBUG_FUNCPTR (gst_yadif_fixate_caps);
+  if (0)
+    base_transform_class->accept_caps =
+        GST_DEBUG_FUNCPTR (gst_yadif_accept_caps);
+  base_transform_class->set_caps = GST_DEBUG_FUNCPTR (gst_yadif_set_caps);
+  if (0)
+    base_transform_class->query = GST_DEBUG_FUNCPTR (gst_yadif_query);
+  if (0)
+    base_transform_class->decide_allocation =
+        GST_DEBUG_FUNCPTR (gst_yadif_decide_allocation);
+  if (0)
+    base_transform_class->filter_meta =
+        GST_DEBUG_FUNCPTR (gst_yadif_filter_meta);
+  if (0)
+    base_transform_class->propose_allocation =
+        GST_DEBUG_FUNCPTR (gst_yadif_propose_allocation);
+  if (0)
+    base_transform_class->transform_size =
+        GST_DEBUG_FUNCPTR (gst_yadif_transform_size);
+  base_transform_class->get_unit_size =
+      GST_DEBUG_FUNCPTR (gst_yadif_get_unit_size);
+  base_transform_class->start = GST_DEBUG_FUNCPTR (gst_yadif_start);
+  base_transform_class->stop = GST_DEBUG_FUNCPTR (gst_yadif_stop);
+  if (0)
+    base_transform_class->sink_event = GST_DEBUG_FUNCPTR (gst_yadif_sink_event);
+  if (0)
+    base_transform_class->src_event = GST_DEBUG_FUNCPTR (gst_yadif_src_event);
+  if (0)
+    base_transform_class->prepare_output_buffer =
+        GST_DEBUG_FUNCPTR (gst_yadif_prepare_output_buffer);
+  if (0)
+    base_transform_class->copy_metadata =
+        GST_DEBUG_FUNCPTR (gst_yadif_copy_metadata);
+  if (0)
+    base_transform_class->transform_meta =
+        GST_DEBUG_FUNCPTR (gst_yadif_transform_meta);
+  if (0)
+    base_transform_class->before_transform =
+        GST_DEBUG_FUNCPTR (gst_yadif_before_transform);
+  base_transform_class->transform = GST_DEBUG_FUNCPTR (gst_yadif_transform);
+  if (0)
+    base_transform_class->transform_ip =
+        GST_DEBUG_FUNCPTR (gst_yadif_transform_ip);
+
+}
+
+static void
+gst_yadif_init (GstYadif * yadif)
+{
+
+  yadif->sinkpad = gst_pad_new_from_static_template (&gst_yadif_sink_template,
+      "sink");
+
+  yadif->srcpad = gst_pad_new_from_static_template (&gst_yadif_src_template,
+      "src");
+}
+
+void
+gst_yadif_set_property (GObject * object, guint property_id,
+    const GValue * value, GParamSpec * pspec)
+{
+  /* GstYadif *yadif = GST_YADIF (object); */
+
+  switch (property_id) {
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+      break;
+  }
+}
+
+void
+gst_yadif_get_property (GObject * object, guint property_id,
+    GValue * value, GParamSpec * pspec)
+{
+  /* GstYadif *yadif = GST_YADIF (object); */
+
+  switch (property_id) {
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+      break;
+  }
+}
+
+void
+gst_yadif_dispose (GObject * object)
+{
+  /* GstYadif *yadif = GST_YADIF (object); */
+
+  /* clean up as possible.  may be called multiple times */
+
+  G_OBJECT_CLASS (gst_yadif_parent_class)->dispose (object);
+}
+
+void
+gst_yadif_finalize (GObject * object)
+{
+  /* GstYadif *yadif = GST_YADIF (object); */
+
+  /* clean up object here */
+
+  G_OBJECT_CLASS (gst_yadif_parent_class)->finalize (object);
+}
+
+
+static GstCaps *
+gst_yadif_transform_caps (GstBaseTransform * trans,
+    GstPadDirection direction, GstCaps * caps, GstCaps * filter)
+{
+  GstCaps *othercaps;
+
+  othercaps = gst_caps_copy (caps);
+
+  if (direction == GST_PAD_SRC) {
+    GValue value = G_VALUE_INIT;
+    GValue v = G_VALUE_INIT;
+
+    g_value_init (&value, GST_TYPE_LIST);
+    g_value_init (&v, G_TYPE_STRING);
+
+    g_value_set_string (&v, "interleaved");
+    gst_value_list_append_value (&value, &v);
+    g_value_set_string (&v, "mixed");
+    gst_value_list_append_value (&value, &v);
+    g_value_set_string (&v, "progressive");
+    gst_value_list_append_value (&value, &v);
+
+    gst_caps_set_value (othercaps, "interlace-mode", &value);
+    g_value_reset (&value);
+    g_value_reset (&v);
+  } else {
+    gst_caps_set_simple (othercaps, "interlace-mode", G_TYPE_STRING,
+        "progressive", NULL);
+  }
+
+  return othercaps;
+}
+
+static GstCaps *
+gst_yadif_fixate_caps (GstBaseTransform * trans,
+    GstPadDirection direction, GstCaps * caps, GstCaps * othercaps)
+{
+
+  return NULL;
+}
+
+static gboolean
+gst_yadif_accept_caps (GstBaseTransform * trans,
+    GstPadDirection direction, GstCaps * caps)
+{
+  return TRUE;
+}
+
+static gboolean
+gst_yadif_set_caps (GstBaseTransform * trans, GstCaps * incaps,
+    GstCaps * outcaps)
+{
+  GstYadif *yadif = GST_YADIF (trans);
+
+  gst_video_info_from_caps (&yadif->video_info, incaps);
+
+  return TRUE;
+}
+
+static gboolean
+gst_yadif_query (GstBaseTransform * trans, GstPadDirection direction,
+    GstQuery * query)
+{
+
+  return TRUE;
+}
+
+static gboolean
+gst_yadif_decide_allocation (GstBaseTransform * trans, GstQuery * query)
+{
+
+  return TRUE;
+}
+
+static gboolean
+gst_yadif_filter_meta (GstBaseTransform * trans, GstQuery * query,
+    GType api, const GstStructure * params)
+{
+
+  return TRUE;
+}
+
+static gboolean
+gst_yadif_propose_allocation (GstBaseTransform * trans,
+    GstQuery * decide_query, GstQuery * query)
+{
+
+  return TRUE;
+}
+
+static gboolean
+gst_yadif_transform_size (GstBaseTransform * trans,
+    GstPadDirection direction,
+    GstCaps * caps, gsize size, GstCaps * othercaps, gsize * othersize)
+{
+
+  return FALSE;
+}
+
+static gboolean
+gst_yadif_get_unit_size (GstBaseTransform * trans, GstCaps * caps, gsize * size)
+{
+  GstVideoInfo info;
+
+  if (gst_video_info_from_caps (&info, caps)) {
+    *size = GST_VIDEO_INFO_SIZE (&info);
+
+    return TRUE;
+  }
+  return FALSE;
+}
+
+static gboolean
+gst_yadif_start (GstBaseTransform * trans)
+{
+
+  return TRUE;
+}
+
+static gboolean
+gst_yadif_stop (GstBaseTransform * trans)
+{
+
+  return TRUE;
+}
+
+static gboolean
+gst_yadif_sink_event (GstBaseTransform * trans, GstEvent * event)
+{
+
+  return TRUE;
+}
+
+static gboolean
+gst_yadif_src_event (GstBaseTransform * trans, GstEvent * event)
+{
+
+  return TRUE;
+}
+
+static GstFlowReturn
+gst_yadif_prepare_output_buffer (GstBaseTransform * trans,
+    GstBuffer * input, GstBuffer ** buf)
+{
+
+  return GST_FLOW_ERROR;
+}
+
+static gboolean
+gst_yadif_copy_metadata (GstBaseTransform * trans,
+    GstBuffer * input, GstBuffer * outbuf)
+{
+
+  return TRUE;
+}
+
+static gboolean
+gst_yadif_transform_meta (GstBaseTransform * trans,
+    GstBuffer * outbuf, GstMeta * meta, GstBuffer * inbuf)
+{
+
+  return TRUE;
+}
+
+static void
+gst_yadif_before_transform (GstBaseTransform * trans, GstBuffer * buffer)
+{
+
+}
+
+void yadif_filter (GstYadif * yadif, int parity, int tff);
+
+static GstFlowReturn
+gst_yadif_transform (GstBaseTransform * trans, GstBuffer * inbuf,
+    GstBuffer * outbuf)
+{
+  GstYadif *yadif = GST_YADIF (trans);
+  int parity;
+  int tff;
+
+  parity = 0;
+  tff = 0;
+
+  if (!gst_video_frame_map (&yadif->dest_frame, &yadif->video_info, outbuf,
+          GST_MAP_WRITE))
+    goto dest_map_failed;
+
+  if (!gst_video_frame_map (&yadif->cur_frame, &yadif->video_info, inbuf,
+          GST_MAP_READ))
+    goto src_map_failed;
+
+  yadif->next_frame = yadif->cur_frame;
+  yadif->prev_frame = yadif->cur_frame;
+
+  yadif_filter (yadif, parity, tff);
+
+  gst_video_frame_unmap (&yadif->dest_frame);
+  gst_video_frame_unmap (&yadif->cur_frame);
+  return GST_FLOW_OK;
+
+dest_map_failed:
+  {
+    GST_ERROR_OBJECT (yadif, "failed to map dest");
+    return GST_FLOW_ERROR;
+  }
+src_map_failed:
+  {
+    GST_ERROR_OBJECT (yadif, "failed to map src");
+    gst_video_frame_unmap (&yadif->dest_frame);
+    return GST_FLOW_ERROR;
+  }
+}
+
+static GstFlowReturn
+gst_yadif_transform_ip (GstBaseTransform * trans, GstBuffer * buf)
+{
+
+  return GST_FLOW_OK;
+}
+
+
+static gboolean
+plugin_init (GstPlugin * plugin)
+{
+
+  return gst_element_register (plugin, "yadif", GST_RANK_NONE, GST_TYPE_YADIF);
+}
+
+GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
+    GST_VERSION_MINOR,
+    yadif,
+    "YADIF deinterlacing filter",
+    plugin_init, VERSION, "GPL", PACKAGE_NAME, GST_PACKAGE_ORIGIN)
diff --git a/gst/yadif/gstyadif.h b/gst/yadif/gstyadif.h
new file mode 100644 (file)
index 0000000..118f501
--- /dev/null
@@ -0,0 +1,64 @@
+/* GStreamer
+ * Copyright (C) 2013 Rdio, Inc. <ingestions@rd.io>
+ * Copyright (C) 2013 David Schleef <ds@schleef.org>
+ *
+ * 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., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef _GST_YADIF_H_
+#define _GST_YADIF_H_
+
+#include <gst/base/gstbasetransform.h>
+#include <gst/video/video.h>
+
+G_BEGIN_DECLS
+
+#define GST_TYPE_YADIF   (gst_yadif_get_type())
+#define GST_YADIF(obj)   (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_YADIF,GstYadif))
+#define GST_YADIF_CLASS(klass)   (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_YADIF,GstYadifClass))
+#define GST_IS_YADIF(obj)   (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_YADIF))
+#define GST_IS_YADIF_CLASS(obj)   (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_YADIF))
+
+typedef struct _GstYadif GstYadif;
+typedef struct _GstYadifClass GstYadifClass;
+
+struct _GstYadif
+{
+  GstBaseTransform base_yadif;
+
+  GstPad *sinkpad;
+  GstPad *srcpad;
+
+  int mode;
+
+  GstVideoInfo video_info;
+
+  GstVideoFrame prev_frame;
+  GstVideoFrame cur_frame;
+  GstVideoFrame next_frame;
+  GstVideoFrame dest_frame;
+};
+
+struct _GstYadifClass
+{
+  GstBaseTransformClass base_yadif_class;
+};
+
+GType gst_yadif_get_type (void);
+
+G_END_DECLS
+
+#endif
diff --git a/gst/yadif/vf_yadif.c b/gst/yadif/vf_yadif.c
new file mode 100644 (file)
index 0000000..efb7884
--- /dev/null
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2013 David Schleef <ds@schleef.org>
+ * Copyright (C) 2013 Rdio, Inc. <ingestions@rd.io>
+ * Copyright (C) 2006-2010 Michael Niedermayer <michaelni@gmx.at>
+ *               2010      James Darnley <james.darnley@gmail.com>
+ *
+ * This file is part of Libav.
+ *
+ * Libav is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Libav 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with Libav; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <gstyadif.h>
+#include <string.h>
+
+#undef NDEBUG
+#include <assert.h>
+
+#define FFABS(a) ABS(a)
+#define FFMIN(a,b) MIN(a,b)
+#define FFMAX(a,b) MAX(a,b)
+#define FFMAX3(a,b,c) FFMAX(FFMAX(a,b),c)
+#define FFMIN3(a,b,c) FFMIN(FFMIN(a,b),c)
+
+
+#define PERM_RWP AV_PERM_WRITE | AV_PERM_PRESERVE | AV_PERM_REUSE
+
+#define CHECK(j)\
+    {   int score = FFABS(cur[mrefs-1+(j)] - cur[prefs-1-(j)])\
+                  + FFABS(cur[mrefs  +(j)] - cur[prefs  -(j)])\
+                  + FFABS(cur[mrefs+1+(j)] - cur[prefs+1-(j)]);\
+        if (score < spatial_score) {\
+            spatial_score= score;\
+            spatial_pred= (cur[mrefs  +(j)] + cur[prefs  -(j)])>>1;\
+
+#define FILTER \
+    for (x = 0;  x < w; x++) { \
+        int c = cur[mrefs]; \
+        int d = (prev2[0] + next2[0])>>1; \
+        int e = cur[prefs]; \
+        int temporal_diff0 = FFABS(prev2[0] - next2[0]); \
+        int temporal_diff1 =(FFABS(prev[mrefs] - c) + FFABS(prev[prefs] - e) )>>1; \
+        int temporal_diff2 =(FFABS(next[mrefs] - c) + FFABS(next[prefs] - e) )>>1; \
+        int diff = FFMAX3(temporal_diff0 >> 1, temporal_diff1, temporal_diff2); \
+        int spatial_pred = (c+e) >> 1; \
+        int spatial_score = FFABS(cur[mrefs - 1] - cur[prefs - 1]) + FFABS(c-e) \
+                          + FFABS(cur[mrefs + 1] - cur[prefs + 1]) - 1; \
+ \
+        CHECK(-1) CHECK(-2) }} }} \
+        CHECK( 1) CHECK( 2) }} }} \
+ \
+        if (mode < 2) { \
+            int b = (prev2[2 * mrefs] + next2[2 * mrefs])>>1; \
+            int f = (prev2[2 * prefs] + next2[2 * prefs])>>1; \
+            int max = FFMAX3(d - e, d - c, FFMIN(b - c, f - e)); \
+            int min = FFMIN3(d - e, d - c, FFMAX(b - c, f - e)); \
+ \
+            diff = FFMAX3(diff, min, -max); \
+        } \
+ \
+        if (spatial_pred > d + diff) \
+           spatial_pred = d + diff; \
+        else if (spatial_pred < d - diff) \
+           spatial_pred = d - diff; \
+ \
+        dst[0] = spatial_pred; \
+ \
+        dst++; \
+        cur++; \
+        prev++; \
+        next++; \
+        prev2++; \
+        next2++; \
+    }
+
+static void
+filter_line_c (guint8 * dst,
+    guint8 * prev, guint8 * cur, guint8 * next,
+    int w, int prefs, int mrefs, int parity, int mode)
+{
+  int x;
+  guint8 *prev2 = parity ? prev : cur;
+  guint8 *next2 = parity ? cur : next;
+
+FILTER}
+
+#if 0
+static void
+filter_line_c_16bit (guint16 * dst,
+    guint16 * prev, guint16 * cur, guint16 * next,
+    int w, int prefs, int mrefs, int parity, int mode)
+{
+  int x;
+  guint16 *prev2 = parity ? prev : cur;
+  guint16 *next2 = parity ? cur : next;
+  mrefs /= 2;
+  prefs /= 2;
+
+FILTER}
+#endif
+
+void yadif_filter (GstYadif * yadif, int parity, int tff);
+void filter_line_x86 (guint8 * dst,
+    guint8 * prev, guint8 * cur, guint8 * next,
+    int w, int prefs, int mrefs, int parity, int mode);
+
+void
+yadif_filter (GstYadif * yadif, int parity, int tff)
+{
+  int y, i;
+  const GstVideoInfo *vi = &yadif->video_info;
+  const GstVideoFormatInfo *vfi = vi->finfo;
+
+  for (i = 0; i < GST_VIDEO_FORMAT_INFO_N_COMPONENTS (vfi); i++) {
+    int w = GST_VIDEO_FORMAT_INFO_SCALE_WIDTH (vfi, i, vi->width);
+    int h = GST_VIDEO_FORMAT_INFO_SCALE_HEIGHT (vfi, i, vi->height);
+    int refs = GST_VIDEO_INFO_COMP_STRIDE (vi, i);
+    int df = GST_VIDEO_INFO_COMP_PSTRIDE (vi, i);
+    guint8 *prev_data = GST_VIDEO_FRAME_COMP_DATA (&yadif->prev_frame, i);
+    guint8 *cur_data = GST_VIDEO_FRAME_COMP_DATA (&yadif->cur_frame, i);
+    guint8 *next_data = GST_VIDEO_FRAME_COMP_DATA (&yadif->next_frame, i);
+    guint8 *dest_data = GST_VIDEO_FRAME_COMP_DATA (&yadif->dest_frame, i);
+
+    for (y = 0; y < h; y++) {
+      if ((y ^ parity) & 1) {
+        guint8 *prev = prev_data + y * refs;
+        guint8 *cur = cur_data + y * refs;
+        guint8 *next = next_data + y * refs;
+        guint8 *dst = dest_data + y * refs;
+        int mode = ((y == 1) || (y + 2 == h)) ? 2 : yadif->mode;
+        if (0) {
+          filter_line_c (dst, prev, cur, next, w,
+              y + 1 < h ? refs : -refs, y ? -refs : refs, parity ^ tff, mode);
+        } else {
+          filter_line_x86 (dst, prev, cur, next, w,
+              y + 1 < h ? refs : -refs, y ? -refs : refs, parity ^ tff, mode);
+        }
+      } else {
+        guint8 *dst = dest_data + y * refs;
+        guint8 *cur = cur_data + y * refs;
+
+        memcpy (dst, cur, w * df);
+      }
+    }
+  }
+
+#if 0
+  emms_c ();
+#endif
+}
diff --git a/gst/yadif/yadif.c b/gst/yadif/yadif.c
new file mode 100644 (file)
index 0000000..93b526d
--- /dev/null
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2006 Michael Niedermayer <michaelni@gmx.at>
+ *
+ * This file is part of Libav.
+ *
+ * Libav is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Libav 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with Libav; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <glib.h>
+#if 0
+#include "libavutil/attributes.h"
+#include "libavutil/cpu.h"
+#include "libavutil/internal.h"
+#include "libavutil/mem.h"
+#include "libavutil/x86/asm.h"
+#include "libavcodec/x86/dsputil_mmx.h"
+#include "libavfilter/yadif.h"
+#endif
+
+//#if HAVE_INLINE_ASM
+
+typedef struct xmm_reg
+{
+  guint64 a, b;
+} xmm_reg;
+typedef gint64 x86_reg;
+#define DECLARE_ALIGNED(n,t,v)      t __attribute__ ((aligned (n))) v
+#define DECLARE_ASM_CONST(n,t,v)    static const t __attribute__((used)) __attribute__ ((aligned (n))) v
+
+#define ARCH_X86_64 1
+//#if ARCH_X86_64 && defined(__PIC__)
+#if 1
+#    define LOCAL_MANGLE(a) #a "(%%rip)"
+#else
+#    define LOCAL_MANGLE(a) #a
+#endif
+
+#define EXTERN_PREFIX ""
+#define MANGLE(a) EXTERN_PREFIX LOCAL_MANGLE(a)
+
+DECLARE_ASM_CONST (16, const xmm_reg, pb_1) = {
+0x0101010101010101ULL, 0x0101010101010101ULL};
+
+DECLARE_ASM_CONST (16, const xmm_reg, pw_1) = {
+0x0001000100010001ULL, 0x0001000100010001ULL};
+
+
+
+#if HAVE_SSSE3_INLINE
+#define COMPILE_TEMPLATE_SSE2 1
+#define COMPILE_TEMPLATE_SSSE3 1
+#undef RENAME
+#define RENAME(a) a ## _ssse3
+#include "yadif_template.c"
+#undef COMPILE_TEMPLATE_SSSE3
+#endif
+
+//#if HAVE_SSE2_INLINE
+#undef RENAME
+#define RENAME(a) a ## _sse2
+#include "yadif_template.c"
+#undef COMPILE_TEMPLATE_SSE2
+//#endif
+
+#if HAVE_MMXEXT_INLINE
+#undef RENAME
+#define RENAME(a) a ## _mmxext
+#include "yadif_template.c"
+#endif
+
+//#endif /* HAVE_INLINE_ASM */
+
+void filter_line_x86 (guint8 * dst,
+    guint8 * prev, guint8 * cur, guint8 * next,
+    int w, int prefs, int mrefs, int parity, int mode);
+
+void
+filter_line_x86 (guint8 * dst,
+    guint8 * prev, guint8 * cur, guint8 * next,
+    int w, int prefs, int mrefs, int parity, int mode)
+{
+#if 0
+#if HAVE_MMXEXT_INLINE
+  if (cpu_flags & AV_CPU_FLAG_MMXEXT)
+    yadif->filter_line = yadif_filter_line_mmxext;
+#endif
+#if HAVE_SSE2_INLINE
+  if (cpu_flags & AV_CPU_FLAG_SSE2)
+    yadif->filter_line = yadif_filter_line_sse2;
+#endif
+#if HAVE_SSSE3_INLINE
+  if (cpu_flags & AV_CPU_FLAG_SSSE3)
+    yadif->filter_line = yadif_filter_line_ssse3;
+#endif
+#endif
+  yadif_filter_line_sse2 (dst, prev, cur, next, w, prefs, mrefs, parity, mode);
+}
diff --git a/gst/yadif/yadif_template.c b/gst/yadif/yadif_template.c
new file mode 100644 (file)
index 0000000..ef3dffc
--- /dev/null
@@ -0,0 +1,262 @@
+/*
+ * Copyright (C) 2006 Michael Niedermayer <michaelni@gmx.at>
+ *
+ * This file is part of Libav.
+ *
+ * Libav is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Libav 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with Libav; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifdef COMPILE_TEMPLATE_SSE2
+#define MM "%%xmm"
+#define MOV  "movq"
+#define MOVQ "movdqa"
+#define MOVQU "movdqu"
+#define STEP 8
+#define LOAD(mem,dst) \
+            MOV"       "mem", "dst" \n\t"\
+            "punpcklbw "MM"7, "dst" \n\t"
+#define PSRL1(reg) "psrldq $1, "reg" \n\t"
+#define PSRL2(reg) "psrldq $2, "reg" \n\t"
+#define PSHUF(src,dst) "movdqa "dst", "src" \n\t"\
+                       "psrldq $2, "src"     \n\t"
+#else
+#define MM "%%mm"
+#define MOV  "movd"
+#define MOVQ "movq"
+#define MOVQU "movq"
+#define STEP 4
+#define LOAD(mem,dst) \
+            MOV"       "mem", "dst" \n\t"\
+            "punpcklbw "MM"7, "dst" \n\t"
+#define PSRL1(reg) "psrlq $8, "reg" \n\t"
+#define PSRL2(reg) "psrlq $16, "reg" \n\t"
+#define PSHUF(src,dst) "pshufw $9, "dst", "src" \n\t"
+#endif
+
+#ifdef COMPILE_TEMPLATE_SSSE3
+#define PABS(tmp,dst) \
+            "pabsw     "dst", "dst" \n\t"
+#else
+#define PABS(tmp,dst) \
+            "pxor     "tmp", "tmp" \n\t"\
+            "psubw    "dst", "tmp" \n\t"\
+            "pmaxsw   "tmp", "dst" \n\t"
+#endif
+
+#define CHECK(pj,mj) \
+            MOVQU" "#pj"(%[cur],%[mrefs]), "MM"2 \n\t" /* cur[x-refs-1+j] */\
+            MOVQU" "#mj"(%[cur],%[prefs]), "MM"3 \n\t" /* cur[x+refs-1-j] */\
+            MOVQ"      "MM"2, "MM"4 \n\t"\
+            MOVQ"      "MM"2, "MM"5 \n\t"\
+            "pxor      "MM"3, "MM"4 \n\t"\
+            "pavgb     "MM"3, "MM"5 \n\t"\
+            "pand     "MANGLE(pb_1)", "MM"4 \n\t"\
+            "psubusb   "MM"4, "MM"5 \n\t"\
+            PSRL1(MM"5")                 \
+            "punpcklbw "MM"7, "MM"5 \n\t" /* (cur[x-refs+j] + cur[x+refs-j])>>1 */\
+            MOVQ"      "MM"2, "MM"4 \n\t"\
+            "psubusb   "MM"3, "MM"2 \n\t"\
+            "psubusb   "MM"4, "MM"3 \n\t"\
+            "pmaxub    "MM"3, "MM"2 \n\t"\
+            MOVQ"      "MM"2, "MM"3 \n\t"\
+            MOVQ"      "MM"2, "MM"4 \n\t" /* ABS(cur[x-refs-1+j] - cur[x+refs-1-j]) */\
+            PSRL1(MM"3")                  /* ABS(cur[x-refs  +j] - cur[x+refs  -j]) */\
+            PSRL2(MM"4")                  /* ABS(cur[x-refs+1+j] - cur[x+refs+1-j]) */\
+            "punpcklbw "MM"7, "MM"2 \n\t"\
+            "punpcklbw "MM"7, "MM"3 \n\t"\
+            "punpcklbw "MM"7, "MM"4 \n\t"\
+            "paddw     "MM"3, "MM"2 \n\t"\
+            "paddw     "MM"4, "MM"2 \n\t"       /* score */
+
+#define CHECK1 \
+            MOVQ"      "MM"0, "MM"3 \n\t"\
+            "pcmpgtw   "MM"2, "MM"3 \n\t" /* if(score < spatial_score) */\
+            "pminsw    "MM"2, "MM"0 \n\t" /* spatial_score= score; */\
+            MOVQ"      "MM"3, "MM"6 \n\t"\
+            "pand      "MM"3, "MM"5 \n\t"\
+            "pandn     "MM"1, "MM"3 \n\t"\
+            "por       "MM"5, "MM"3 \n\t"\
+            MOVQ"      "MM"3, "MM"1 \n\t"       /* spatial_pred= (cur[x-refs+j] + cur[x+refs-j])>>1; */
+
+#define CHECK2 /* pretend not to have checked dir=2 if dir=1 was bad.\
+                  hurts both quality and speed, but matches the C version. */\
+            "paddw    "MANGLE(pw_1)", "MM"6 \n\t"\
+            "psllw     $14,   "MM"6 \n\t"\
+            "paddsw    "MM"6, "MM"2 \n\t"\
+            MOVQ"      "MM"0, "MM"3 \n\t"\
+            "pcmpgtw   "MM"2, "MM"3 \n\t"\
+            "pminsw    "MM"2, "MM"0 \n\t"\
+            "pand      "MM"3, "MM"5 \n\t"\
+            "pandn     "MM"1, "MM"3 \n\t"\
+            "por       "MM"5, "MM"3 \n\t"\
+            MOVQ"      "MM"3, "MM"1 \n\t"
+
+static void RENAME (yadif_filter_line) (guint8 * dst, guint8 * prev,
+    guint8 * cur, guint8 * next, int w, int prefs, int mrefs, int parity,
+    int mode)
+{
+  DECLARE_ALIGNED (16, guint8, tmp)[16 * 4];
+  int x;
+
+#define FILTER\
+    for(x=0; x<w; x+=STEP){\
+        __asm__ volatile(\
+            "pxor      "MM"7, "MM"7 \n\t"\
+            LOAD("(%[cur],%[mrefs])", MM"0") /* c = cur[x-refs] */\
+            LOAD("(%[cur],%[prefs])", MM"1") /* e = cur[x+refs] */\
+            LOAD("(%["prev2"])", MM"2") /* prev2[x] */\
+            LOAD("(%["next2"])", MM"3") /* next2[x] */\
+            MOVQ"      "MM"3, "MM"4 \n\t"\
+            "paddw     "MM"2, "MM"3 \n\t"\
+            "psraw     $1,    "MM"3 \n\t" /* d = (prev2[x] + next2[x])>>1 */\
+            MOVQ"      "MM"0,   (%[tmp]) \n\t" /* c */\
+            MOVQ"      "MM"3, 16(%[tmp]) \n\t" /* d */\
+            MOVQ"      "MM"1, 32(%[tmp]) \n\t" /* e */\
+            "psubw     "MM"4, "MM"2 \n\t"\
+            PABS(      MM"4", MM"2") /* temporal_diff0 */\
+            LOAD("(%[prev],%[mrefs])", MM"3") /* prev[x-refs] */\
+            LOAD("(%[prev],%[prefs])", MM"4") /* prev[x+refs] */\
+            "psubw     "MM"0, "MM"3 \n\t"\
+            "psubw     "MM"1, "MM"4 \n\t"\
+            PABS(      MM"5", MM"3")\
+            PABS(      MM"5", MM"4")\
+            "paddw     "MM"4, "MM"3 \n\t" /* temporal_diff1 */\
+            "psrlw     $1,    "MM"2 \n\t"\
+            "psrlw     $1,    "MM"3 \n\t"\
+            "pmaxsw    "MM"3, "MM"2 \n\t"\
+            LOAD("(%[next],%[mrefs])", MM"3") /* next[x-refs] */\
+            LOAD("(%[next],%[prefs])", MM"4") /* next[x+refs] */\
+            "psubw     "MM"0, "MM"3 \n\t"\
+            "psubw     "MM"1, "MM"4 \n\t"\
+            PABS(      MM"5", MM"3")\
+            PABS(      MM"5", MM"4")\
+            "paddw     "MM"4, "MM"3 \n\t" /* temporal_diff2 */\
+            "psrlw     $1,    "MM"3 \n\t"\
+            "pmaxsw    "MM"3, "MM"2 \n\t"\
+            MOVQ"      "MM"2, 48(%[tmp]) \n\t" /* diff */\
+\
+            "paddw     "MM"0, "MM"1 \n\t"\
+            "paddw     "MM"0, "MM"0 \n\t"\
+            "psubw     "MM"1, "MM"0 \n\t"\
+            "psrlw     $1,    "MM"1 \n\t" /* spatial_pred */\
+            PABS(      MM"2", MM"0")      /* ABS(c-e) */\
+\
+            MOVQU" -1(%[cur],%[mrefs]), "MM"2 \n\t" /* cur[x-refs-1] */\
+            MOVQU" -1(%[cur],%[prefs]), "MM"3 \n\t" /* cur[x+refs-1] */\
+            MOVQ"      "MM"2, "MM"4 \n\t"\
+            "psubusb   "MM"3, "MM"2 \n\t"\
+            "psubusb   "MM"4, "MM"3 \n\t"\
+            "pmaxub    "MM"3, "MM"2 \n\t"\
+            PSHUF(MM"3", MM"2") \
+            "punpcklbw "MM"7, "MM"2 \n\t" /* ABS(cur[x-refs-1] - cur[x+refs-1]) */\
+            "punpcklbw "MM"7, "MM"3 \n\t" /* ABS(cur[x-refs+1] - cur[x+refs+1]) */\
+            "paddw     "MM"2, "MM"0 \n\t"\
+            "paddw     "MM"3, "MM"0 \n\t"\
+            "psubw    "MANGLE(pw_1)", "MM"0 \n\t" /* spatial_score */\
+\
+            CHECK(-2,0)\
+            CHECK1\
+            CHECK(-3,1)\
+            CHECK2\
+            CHECK(0,-2)\
+            CHECK1\
+            CHECK(1,-3)\
+            CHECK2\
+\
+            /* if(p->mode<2) ... */\
+            MOVQ" 48(%[tmp]), "MM"6 \n\t" /* diff */\
+            "cmpl      $2, %[mode] \n\t"\
+            "jge       1f \n\t"\
+            LOAD("(%["prev2"],%[mrefs],2)", MM"2") /* prev2[x-2*refs] */\
+            LOAD("(%["next2"],%[mrefs],2)", MM"4") /* next2[x-2*refs] */\
+            LOAD("(%["prev2"],%[prefs],2)", MM"3") /* prev2[x+2*refs] */\
+            LOAD("(%["next2"],%[prefs],2)", MM"5") /* next2[x+2*refs] */\
+            "paddw     "MM"4, "MM"2 \n\t"\
+            "paddw     "MM"5, "MM"3 \n\t"\
+            "psrlw     $1,    "MM"2 \n\t" /* b */\
+            "psrlw     $1,    "MM"3 \n\t" /* f */\
+            MOVQ"   (%[tmp]), "MM"4 \n\t" /* c */\
+            MOVQ" 16(%[tmp]), "MM"5 \n\t" /* d */\
+            MOVQ" 32(%[tmp]), "MM"7 \n\t" /* e */\
+            "psubw     "MM"4, "MM"2 \n\t" /* b-c */\
+            "psubw     "MM"7, "MM"3 \n\t" /* f-e */\
+            MOVQ"      "MM"5, "MM"0 \n\t"\
+            "psubw     "MM"4, "MM"5 \n\t" /* d-c */\
+            "psubw     "MM"7, "MM"0 \n\t" /* d-e */\
+            MOVQ"      "MM"2, "MM"4 \n\t"\
+            "pminsw    "MM"3, "MM"2 \n\t"\
+            "pmaxsw    "MM"4, "MM"3 \n\t"\
+            "pmaxsw    "MM"5, "MM"2 \n\t"\
+            "pminsw    "MM"5, "MM"3 \n\t"\
+            "pmaxsw    "MM"0, "MM"2 \n\t" /* max */\
+            "pminsw    "MM"0, "MM"3 \n\t" /* min */\
+            "pxor      "MM"4, "MM"4 \n\t"\
+            "pmaxsw    "MM"3, "MM"6 \n\t"\
+            "psubw     "MM"2, "MM"4 \n\t" /* -max */\
+            "pmaxsw    "MM"4, "MM"6 \n\t" /* diff= MAX3(diff, min, -max); */\
+            "1: \n\t"\
+\
+            MOVQ" 16(%[tmp]), "MM"2 \n\t" /* d */\
+            MOVQ"      "MM"2, "MM"3 \n\t"\
+            "psubw     "MM"6, "MM"2 \n\t" /* d-diff */\
+            "paddw     "MM"6, "MM"3 \n\t" /* d+diff */\
+            "pmaxsw    "MM"2, "MM"1 \n\t"\
+            "pminsw    "MM"3, "MM"1 \n\t" /* d = clip(spatial_pred, d-diff, d+diff); */\
+            "packuswb  "MM"1, "MM"1 \n\t"\
+\
+            ::[prev] "r"(prev),\
+             [cur]  "r"(cur),\
+             [next] "r"(next),\
+             [prefs]"r"((x86_reg)prefs),\
+             [mrefs]"r"((x86_reg)mrefs),\
+             [mode] "g"(mode),\
+             [tmp]  "r"(tmp)\
+        );\
+        __asm__ volatile(MOV" "MM"1, %0" :"=m"(*dst));\
+        dst += STEP;\
+        prev+= STEP;\
+        cur += STEP;\
+        next+= STEP;\
+    }
+
+  if (parity) {
+#define prev2 "prev"
+#define next2 "cur"
+    FILTER
+#undef prev2
+#undef next2
+  } else {
+#define prev2 "cur"
+#define next2 "next"
+    FILTER
+#undef prev2
+#undef next2
+  }
+}
+
+#undef STEP
+#undef MM
+#undef MOV
+#undef MOVQ
+#undef MOVQU
+#undef PSHUF
+#undef PSRL1
+#undef PSRL2
+#undef LOAD
+#undef PABS
+#undef CHECK
+#undef CHECK1
+#undef CHECK2
+#undef FILTER