--- configure.ac	2005-02-09 14:22:07.188729021 +0100
+++ configure.ac	2005-02-09 14:23:44.906379213 +0100
@@ -385,6 +385,7 @@
 	flx \
 	goom \
 	interleave \
+	imagemixer \
 	law \
 	level \
 	matroska \
@@ -1967,6 +1968,7 @@
 gst/games/Makefile
 gst/goom/Makefile
 gst/interleave/Makefile
+gst/imagemixer/Makefile
 gst/law/Makefile
 gst/level/Makefile
 gst/matroska/Makefile
--- gst/imagemixer/gstblend.c	1970-01-01 01:00:00.000000000 +0100
+++ gst/imagemixer/gstblend.c	2005-02-09 14:21:53.627859207 +0100
@@ -0,0 +1,102 @@
+/* Generic video mixer plugin
+ * Copyright (C) 2004 Wim Taymans <wim@fluendo.com>,
+ *               2005 Gergely Nagy <gergely.nagy@neteyes.hu>
+ *
+ * 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.
+ */
+
+#include <gst/gst.h>
+#include "gstblend.h"
+
+static gint
+_compare (GstBlendDefinition *def1, GstBlendDefinition *def2)
+{
+  return (def1->type - def2->type);
+}
+
+static const GList *
+gst_blend_get_definitions (void)
+{
+  static GList *defs = NULL;
+  static GstBlendDefinition definitions[] = {
+    {GST_BLEND_MODE_NORMAL, "normal", "Normal blending"},
+    {GST_BLEND_MODE_ADD, "add", "Additive blending"},
+    {GST_BLEND_MODE_SUBTRACT, "subtract", "Subtractive blending"},
+    {GST_BLEND_MODE_DARKEN, "darken", "Darken"},
+    {GST_BLEND_MODE_LIGHTEN, "lighten", "Lighten"},
+    {GST_BLEND_MODE_MULTIPLY, "multiply", "Multiply"},
+    {GST_BLEND_MODE_DIFFERENCE, "difference", "Difference"},
+    {GST_BLEND_MODE_EXCLUSION, "exclusion", "Exclusion"},
+    {GST_BLEND_MODE_SOFT_LIGHT, "soft_light", "Soft light"},
+    {GST_BLEND_MODE_HARD_LIGHT, "hard_light", "Hard light"},
+    {GST_BLEND_MODE_COLOR, "color", "Color only"},
+    {GST_BLEND_MODE_NULL, NULL, NULL},
+  };
+  gint i = 0;
+
+  if (defs == NULL) {
+    while (definitions[i].short_name) {
+      defs = g_list_insert_sorted (defs, &definitions[i],
+				   (GCompareFunc)_compare);
+      i++;
+    }
+  }
+
+  return defs;
+}
+
+GType
+gst_blend_mode_get_type (void)
+{
+  static GType blend_mode = 0;
+  GEnumValue *blend_modes;
+
+  if (!blend_mode) {
+    const GList *definitions;
+    gint i;
+
+    definitions = gst_blend_get_definitions ();
+    blend_modes =
+      g_new0 (GEnumValue, g_list_length ((GList *)definitions) + 1);
+
+    while (definitions) {
+      GstBlendDefinition *definition =
+	(GstBlendDefinition *)definitions->data;
+
+      definitions = g_list_next (definitions);
+
+      blend_modes[i].value = definition->type;
+      blend_modes[i].value_name = definition->short_name;
+      blend_modes[i].value_nick = definition->long_name;
+
+      i++;
+    }
+
+    blend_mode =
+      g_enum_register_static ("GstBlendMode", blend_modes);
+  }
+
+  return blend_mode;
+}
+
+/*
+ * Local variables:
+ * mode: c
+ * file-style: k&r
+ * c-basic-offset: 2
+ * arch-tag: b18f4406-3e12-4d90-8c7c-5ea99b9c4a6f
+ * End:
+ */
--- gst/imagemixer/gstblend.h	1970-01-01 01:00:00.000000000 +0100
+++ gst/imagemixer/gstblend.h	2005-02-09 14:21:53.627859207 +0100
@@ -0,0 +1,208 @@
+/* Generic video mixer plugin
+ * Copyright (C) 2004 Wim Taymans <wim@fluendo.com>,
+ *               2005 Gergely Nagy <gergely.nagy@neteyes.hu>
+ *
+ * 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_BLEND_H__
+#define __GST_BLEND_H__ 1
+
+#include <gst/gst.h>
+
+G_BEGIN_DECLS
+
+typedef struct _GstBlendDefinition GstBlendDefinition;
+typedef enum _GstBlendModes GstBlendModes;
+
+struct _GstBlendDefinition
+{
+  gint type;
+  gchar *short_name;
+  gchar *long_name;
+};
+
+enum _GstBlendModes
+{
+  GST_BLEND_MODE_NULL,
+  GST_BLEND_MODE_NORMAL,
+  GST_BLEND_MODE_ADD,
+  GST_BLEND_MODE_SUBTRACT,
+  GST_BLEND_MODE_DARKEN,
+  GST_BLEND_MODE_LIGHTEN,
+  GST_BLEND_MODE_MULTIPLY,
+  GST_BLEND_MODE_DIFFERENCE,
+  GST_BLEND_MODE_EXCLUSION,
+  GST_BLEND_MODE_SOFT_LIGHT,
+  GST_BLEND_MODE_HARD_LIGHT,
+  GST_BLEND_MODE_COLOR,
+};
+
+#define GST_TYPE_BLEND_MODE (gst_blend_mode_get_type ())
+
+GType gst_blend_mode_get_type (void);
+
+#define BLEND_NORMAL(Y1,U1,V1,Y2,U2,V2,alpha,Y,U,V)	\
+  Y = ((Y1*(255-alpha))+(Y2*alpha))>>8;			\
+  U = ((U1*(255-alpha))+(U2*alpha))>>8;			\
+  V = ((V1*(255-alpha))+(V2*alpha))>>8;
+
+#define BLEND_ADD(Y1,U1,V1,Y2,U2,V2,alpha,Y,U,V)		\
+  Y = Y1+((Y2*alpha)>>8);					\
+  U = U1+(((127*(255-alpha)+(U2*alpha)))>>8)-127;		\
+  V = V1+(((127*(255-alpha)+(V2*alpha)))>>8)-127;		\
+  if (Y>255) {							\
+    gint mult = MAX (0, 288-Y);					\
+    U = ((U*mult) + (127*(32-mult)))>>5;			\
+    V = ((V*mult) + (127*(32-mult)))>>5;			\
+    Y = 255;							\
+  }								\
+  U = MIN (U,255);						\
+  V = MIN (V,255);
+
+#define BLEND_SUBTRACT(Y1,U1,V1,Y2,U2,V2,alpha,Y,U,V)		\
+  Y = Y1-((Y2*alpha)>>8);					\
+  U = U1+(((127*(255-alpha)+(U2*alpha)))>>8)-127;		\
+  V = V1+(((127*(255-alpha)+(V2*alpha)))>>8)-127;		\
+  if (Y<0) {							\
+    gint mult = MIN (32, -Y);					\
+    U = ((U*(32-mult)) + (127*mult))>>5;			\
+    V = ((V*(32-mult)) + (127*mult))>>5;			\
+    Y = 0;							\
+  }
+
+#define BLEND_DARKEN(Y1,U1,V1,Y2,U2,V2,alpha,Y,U,V)	\
+  if (Y1 < Y2) {					\
+    Y = Y1; U = U1; V = V1;				\
+  }							\
+  else {						\
+    Y = ((Y1*(255-alpha))+(Y2*alpha))>>8;		\
+    U = ((U1*(255-alpha))+(U2*alpha))>>8;		\
+    V = ((V1*(255-alpha))+(V2*alpha))>>8;		\
+  }
+
+#define BLEND_LIGHTEN(Y1,U1,V1,Y2,U2,V2,alpha,Y,U,V)	\
+  if (Y1 > Y2) {					\
+    Y = Y1; U = U1; V = V1;				\
+  }							\
+  else {						\
+    Y = ((Y1*(255-alpha))+(Y2*alpha))>>8;		\
+    U = ((U1*(255-alpha))+(U2*alpha))>>8;		\
+    V = ((V1*(255-alpha))+(V2*alpha))>>8;		\
+  }
+
+#define BLEND_MULTIPLY(Y1,U1,V1,Y2,U2,V2,alpha,Y,U,V)		\
+  Y = (Y1*(256*(255-alpha) +(Y2*alpha)))>>16;			\
+  U = ((U1*(255-alpha)*256)+(alpha*(U1*Y2+128*(256-Y2))))>>16;	\
+  V = ((V1*(255-alpha)*256)+(alpha*(V1*Y2+128*(256-Y2))))>>16;
+
+#define BLEND_DIFFERENCE(Y1,U1,V1,Y2,U2,V2,alpha,Y,U,V)		\
+  Y = ABS((gint)Y1-(gint)Y2)+127;				\
+  U = ABS((gint)U1-(gint)U2)+127;				\
+  V = ABS((gint)V1-(gint)V2)+127;				\
+  Y = ((Y*alpha)+(Y1*(255-alpha)))>>8;				\
+  U = ((U*alpha)+(U1*(255-alpha)))>>8;				\
+  V = ((V*alpha)+(V1*(255-alpha)))>>8;				\
+  if (Y>255) {							\
+    gint mult = MAX (0, 288-Y);					\
+    U = ((U*mult) + (127*(32-mult)))>>5;			\
+    V = ((V*mult) + (127*(32-mult)))>>5;			\
+    Y = 255;							\
+  } else if (Y<0) {						\
+    gint mult = MIN (32, -Y);					\
+    U = ((U*(32-mult)) + (127*mult))>>5;			\
+    V = ((V*(32-mult)) + (127*mult))>>5;			\
+    Y = 0;							\
+  }								\
+  U = CLAMP(U, 0, 255);						\
+  V = CLAMP(V, 0, 255);
+
+#define BLEND_EXCLUSION(Y1,U1,V1,Y2,U2,V2,alpha,Y,U,V)		\
+  Y = ((gint)(Y1^0xff)*Y2+(gint)(Y2^0xff)*Y1)>>8;		\
+  U = ((gint)(U1^0xff)*Y2+(gint)(Y2^0xff)*U1)>>8;		\
+  V = ((gint)(V1^0xff)*Y2+(gint)(Y2^0xff)*V1)>>8;		\
+  Y = ((Y*alpha)+(Y1*(255-alpha)))>>8;				\
+  U = ((U*alpha)+(U1*(255-alpha)))>>8;				\
+  V = ((V*alpha)+(V1*(255-alpha)))>>8;				\
+  if (Y>255) {							\
+    gint mult = MAX (0, 288-Y);					\
+    U = ((U*mult) + (127*(32-mult)))>>5;			\
+    V = ((V*mult) + (127*(32-mult)))>>5;			\
+    Y = 255;							\
+  } else if (Y<0) {						\
+    gint mult = MIN (32, -Y);					\
+    U = ((U*(32-mult)) + (127*mult))>>5;			\
+    V = ((V*(32-mult)) + (127*mult))>>5;			\
+    Y = 0;							\
+  }								\
+  U = CLAMP(U, 0, 255);						\
+  V = CLAMP(V, 0, 255);
+
+#define BLEND_SOFTLIGHT(Y1,U1,V1,Y2,U2,V2,alpha,Y,U,V)		\
+  Y = (gint)Y1+(gint)Y2 - 127;					\
+  U = (gint)U1+(gint)U2 - 127;					\
+  V = (gint)V1+(gint)V2 - 127;					\
+  Y = ((Y*alpha)+(Y1*(255-alpha)))>>8;				\
+  U = ((U*alpha)+(U1*(255-alpha)))>>8;				\
+  V = ((V*alpha)+(V1*(255-alpha)))>>8;				\
+  if (Y>255) {							\
+    gint mult = MAX (0, 288-Y);					\
+    U = ((U*mult) + (127*(32-mult)))>>5;			\
+    V = ((V*mult) + (127*(32-mult)))>>5;			\
+    Y = 255;							\
+  } else if (Y<0) {						\
+    gint mult = MIN (32, -Y);					\
+    U = ((U*(32-mult)) + (127*mult))>>5;			\
+    V = ((V*(32-mult)) + (127*mult))>>5;			\
+    Y = 0;							\
+  }								\
+
+#define BLEND_HARDLIGHT(Y1,U1,V1,Y2,U2,V2,alpha,Y,U,V)		\
+  Y = (gint)Y1+(gint)Y2*2 - 255;				\
+  U = (gint)U1+(gint)U2 - 127;					\
+  V = (gint)V1+(gint)V2 - 127;					\
+  Y = ((Y*alpha)+(Y1*(255-alpha)))>>8;				\
+  U = ((U*alpha)+(U1*(255-alpha)))>>8;				\
+  V = ((V*alpha)+(V1*(255-alpha)))>>8;				\
+  if (Y>255) {							\
+    gint mult = MAX (0, 288-Y);					\
+    U = ((U*mult) + (127*(32-mult)))>>5;			\
+    V = ((V*mult) + (127*(32-mult)))>>5;			\
+    Y = 255;							\
+  } else if (Y<0) {						\
+    gint mult = MIN (32, -Y);					\
+    U = ((U*(32-mult)) + (127*mult))>>5;			\
+    V = ((V*(32-mult)) + (127*mult))>>5;			\
+    Y = 0;							\
+  }								\
+
+#define BLEND_COLOR(Y1,U1,V1,Y2,U2,V2,alpha,Y,U,V)		\
+  Y = Y1;							\
+  U = ((U1*(255-alpha))+(U2*alpha))>>8;				\
+  V = ((V1*(255-alpha))+(V2*alpha))>>8;
+
+G_END_DECLS
+
+#endif
+
+/*
+ * Local variables:
+ * mode: c
+ * file-style: k&r
+ * c-basic-offset: 2
+ * arch-tag: 96724262-aa4c-44b5-a131-904f6e1cc0d1
+ * End:
+ */
--- gst/imagemixer/gstimagemixer.c	1970-01-01 01:00:00.000000000 +0100
+++ gst/imagemixer/gstimagemixer.c	2005-02-09 14:21:53.627859207 +0100
@@ -0,0 +1,502 @@
+/* Generic video mixer plugin
+ * Copyright (C) 2004 Wim Taymans <wim@fluendo.com>,
+ *               2005 Gergely Nagy <gergely.nagy@neteyes.hu>
+ *
+ * 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.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <gst/gst.h>
+#include <gst/video/video.h>
+
+#include "gstimagemixer.h"
+#include "gstblend.h"
+
+GST_DEBUG_CATEGORY (imagemixer_debug);
+#define GST_CAT_DEFAULT imagemixer_debug
+
+static GstElementDetails imagemixer_details =
+GST_ELEMENT_DETAILS ("Image mixer",
+		     "Filter/Editor/Video",
+		     "Mix two images",
+		     "Wim Taymans <wim@fluendo.com>, "
+		     "Gergely Nagy <gergely.nagy@neteyes.hu>");
+
+GstStaticPadTemplate gst_imagemixer_src_template =
+  GST_STATIC_PAD_TEMPLATE
+  ("src", GST_PAD_SRC, GST_PAD_ALWAYS,
+   GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("AYUV")));
+
+GstStaticPadTemplate gst_imagemixer_bg_template =
+  GST_STATIC_PAD_TEMPLATE
+  ("background", GST_PAD_SINK, GST_PAD_ALWAYS,
+   GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("AYUV")));
+
+GstStaticPadTemplate gst_imagemixer_fg_template =
+  GST_STATIC_PAD_TEMPLATE
+  ("foreground", GST_PAD_SINK, GST_PAD_ALWAYS,
+   GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("AYUV")));
+
+
+enum {
+  ARG_0,
+  ARG_XPOS,
+  ARG_YPOS,
+  ARG_MODE,
+};
+
+static void gst_imagemixer_base_init (gpointer g_class);
+static void gst_imagemixer_class_init (GstImageMixerClass *class);
+static void gst_imagemixer_init (GstImageMixer *imagemixer);
+static void gst_imagemixer_loop (GstElement *element);
+
+static GstElementClass *parent_class = NULL;
+
+GType
+gst_imagemixer_get_type (void)
+{
+  static GType imagemixer_type = 0;
+
+  if (!imagemixer_type) {
+    static const GTypeInfo imagemixer_info = {
+      sizeof (GstImageMixerClass),
+      gst_imagemixer_base_init,
+      NULL,
+      (GClassInitFunc) gst_imagemixer_class_init,
+      NULL,
+      NULL,
+      sizeof (GstImageMixer),
+      0,
+      (GInstanceInitFunc) gst_imagemixer_init,
+    };
+
+    imagemixer_type = g_type_register_static
+      (GST_TYPE_ELEMENT, "GstImageMixer", &imagemixer_info, 0);
+  }
+
+  return imagemixer_type;
+}
+
+void
+gst_imagemixer_set_property (GObject *object, guint prop_id,
+			     const GValue *value, GParamSpec *pspec)
+{
+  GstImageMixer *imagemixer = GST_IMAGEMIXER (object);
+
+  switch (prop_id) {
+  case ARG_XPOS:
+    imagemixer->xpos = g_value_get_int (value);
+    break;
+  case ARG_YPOS:
+    imagemixer->ypos = g_value_get_int (value);
+    break;
+  case ARG_MODE:
+    imagemixer->mode = g_value_get_enum (value);
+    break;
+  default:
+    G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    break;
+  }
+}
+
+void
+gst_imagemixer_get_property (GObject *object, guint prop_id,
+			     GValue *value, GParamSpec *pspec)
+{
+  GstImageMixer *imagemixer = GST_IMAGEMIXER (object);
+
+  switch (prop_id) {
+  case ARG_XPOS:
+    g_value_set_int (value, imagemixer->xpos);
+    break;
+  case ARG_YPOS:
+    g_value_set_int (value, imagemixer->ypos);
+    break;
+  case ARG_MODE:
+    g_value_set_enum (value, imagemixer->mode);
+    break;
+  default:
+    G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    break;
+  }
+}
+
+GstPadLinkReturn
+gst_imagemixer_link (GstPad *pad, const GstCaps *caps)
+{
+  GstImageMixer *imagemixer;
+  GstStructure *structure = gst_caps_get_structure (caps, 0);
+
+  imagemixer = GST_IMAGEMIXER (gst_pad_get_parent (pad));
+
+  if (pad == imagemixer->bgpad) {
+    gst_structure_get_int (structure, "width", &imagemixer->bg.width);
+    gst_structure_get_int (structure, "height", &imagemixer->bg.height);
+    gst_structure_get_double (structure, "framerate", &imagemixer->bg.rate);
+
+    return gst_pad_proxy_link (pad, caps);
+  }
+
+  if (pad == imagemixer->fgpad) {
+    gst_structure_get_int (structure, "width", &imagemixer->fg.width);
+    gst_structure_get_int (structure, "height", &imagemixer->fg.height);
+    gst_structure_get_double (structure, "framerate", &imagemixer->fg.rate);
+
+    return gst_pad_proxy_link (pad, caps);
+  }
+
+  if (pad == imagemixer->srcpad) {
+    gst_structure_get_int (structure, "width", &imagemixer->src.width);
+    gst_structure_get_int (structure, "height", &imagemixer->src.height);
+    gst_structure_get_double (structure, "framerate", &imagemixer->src.rate);
+
+    return gst_pad_proxy_link (pad, caps);
+  }
+
+  return GST_PAD_LINK_REFUSED;
+}
+
+static void
+gst_imagemixer_base_init (gpointer g_class)
+{
+  GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
+
+  gst_element_class_set_details (element_class, &imagemixer_details);
+
+  gst_element_class_add_pad_template
+    (element_class, gst_static_pad_template_get (&gst_imagemixer_bg_template));
+  gst_element_class_add_pad_template
+    (element_class, gst_static_pad_template_get (&gst_imagemixer_fg_template));
+  gst_element_class_add_pad_template
+    (element_class, gst_static_pad_template_get
+     (&gst_imagemixer_src_template));
+}
+
+static void
+gst_imagemixer_class_init (GstImageMixerClass *class)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (class);
+  GstElementClass *element_class = GST_ELEMENT_CLASS (class);
+
+  parent_class = g_type_class_peek_parent (class);
+
+  g_object_class_install_property
+    (object_class, ARG_XPOS,
+     g_param_spec_int ("xpos", "X position",
+			"X position",
+			G_MININT, G_MAXINT, 0, G_PARAM_READWRITE));
+  g_object_class_install_property
+    (object_class, ARG_YPOS,
+     g_param_spec_int ("ypos", "Y position",
+		       "Y position",
+		       G_MININT, G_MAXINT, 0, G_PARAM_READWRITE));
+  g_object_class_install_property
+    (object_class, ARG_MODE,
+     g_param_spec_enum ("mode", "Mode", "Blending mode",
+			GST_TYPE_BLEND_MODE, 1, G_PARAM_READWRITE));
+
+  object_class->set_property = gst_imagemixer_set_property;
+  object_class->get_property = gst_imagemixer_get_property;
+}
+
+static void
+gst_imagemixer_init (GstImageMixer *imagemixer)
+{
+  GST_FLAG_SET (imagemixer, GST_ELEMENT_EVENT_AWARE);
+
+  imagemixer->bgpad = gst_pad_new_from_template
+    (gst_static_pad_template_get (&gst_imagemixer_bg_template),
+     "background");
+  gst_element_add_pad (GST_ELEMENT (imagemixer), imagemixer->bgpad);
+  gst_pad_set_link_function (imagemixer->bgpad, gst_imagemixer_link);
+
+  imagemixer->fgpad = gst_pad_new_from_template
+    (gst_static_pad_template_get (&gst_imagemixer_fg_template),
+     "foreground");
+  gst_element_add_pad (GST_ELEMENT (imagemixer), imagemixer->fgpad);
+  gst_pad_set_link_function (imagemixer->fgpad, gst_imagemixer_link);
+
+  imagemixer->srcpad = gst_pad_new_from_template
+    (gst_static_pad_template_get (&gst_imagemixer_src_template), "src");
+  gst_element_add_pad (GST_ELEMENT (imagemixer), imagemixer->srcpad);
+  gst_pad_set_link_function (imagemixer->srcpad, gst_imagemixer_link);
+
+  gst_element_set_loop_function (GST_ELEMENT (imagemixer),
+				 gst_imagemixer_loop);
+
+  imagemixer->xpos = 0;
+  imagemixer->ypos = 0;
+  imagemixer->fg_buffer = NULL;
+  imagemixer->mode = 1;
+
+  imagemixer->fg.width = 0;
+  imagemixer->fg.height = 0;
+  imagemixer->fg.rate = 0.0;
+
+  imagemixer->bg.width = 0;
+  imagemixer->bg.height = 0;
+  imagemixer->bg.rate = 0.0;
+
+  imagemixer->src.width = 0;
+  imagemixer->src.height = 0;
+  imagemixer->src.rate = 0.0;
+}
+
+void
+gst_imagemixer_mix (GstImageMixer *imagemixer, GstBuffer *bg, GstBuffer *fg)
+{
+  gint x, y;
+  guint8 *bgd, *fgd;
+  gint fgh, fgw, bgh, bgw;
+  GstStructure *s;
+
+  fgh = imagemixer->fg.height;
+  fgw = imagemixer->fg.width;
+  bgh = imagemixer->bg.height;
+  bgw = imagemixer->bg.width;
+
+  bgd = GST_BUFFER_DATA (bg);
+  fgd = GST_BUFFER_DATA (fg);
+
+  for (y = 0; (y < fgh) && (y + imagemixer->ypos < bgh); y++) {
+
+    if (y + imagemixer->ypos < 0)
+      continue;
+
+    for (x = 0; (x < fgw) && (x + imagemixer->xpos < bgw); x++) {
+      unsigned int bg_pos = (y + imagemixer->ypos) * bgw * 4 +
+	(x + imagemixer->xpos) * 4;
+      unsigned int fg_pos = y * fgw * 4 + x * 4;
+      gint alpha = fgd[fg_pos];
+      gint Y, U, V;
+
+      if (x + imagemixer->xpos < 0)
+	continue;
+
+      if (fgd[fg_pos] == 0)
+	continue;
+
+      switch (imagemixer->mode) {
+      case GST_BLEND_MODE_NORMAL:
+	BLEND_NORMAL
+	  ((gint)bgd[bg_pos + 1], (gint)bgd[bg_pos + 2], (gint)bgd[bg_pos + 3],
+	   (gint)fgd[fg_pos + 1], (gint)fgd[fg_pos + 2], (gint)fgd[fg_pos + 3],
+	   alpha, Y, U, V);
+	break;
+      case GST_BLEND_MODE_ADD:
+	BLEND_ADD
+	  ((gint)bgd[bg_pos + 1], (gint)bgd[bg_pos + 2], (gint)bgd[bg_pos + 3],
+	   (gint)fgd[fg_pos + 1], (gint)fgd[fg_pos + 2], (gint)fgd[fg_pos + 3],
+	   alpha, Y, U, V);
+	break;
+      case GST_BLEND_MODE_SUBTRACT:
+	BLEND_SUBTRACT
+	  ((gint)bgd[bg_pos + 1], (gint)bgd[bg_pos + 2], (gint)bgd[bg_pos + 3],
+	   (gint)fgd[fg_pos + 1], (gint)fgd[fg_pos + 2], (gint)fgd[fg_pos + 3],
+	   alpha, Y, U, V);
+	break;
+      case GST_BLEND_MODE_DARKEN:
+	BLEND_DARKEN
+	  ((gint)bgd[bg_pos + 1], (gint)bgd[bg_pos + 2], (gint)bgd[bg_pos + 3],
+	   (gint)fgd[fg_pos + 1], (gint)fgd[fg_pos + 2], (gint)fgd[fg_pos + 3],
+	   alpha, Y, U, V);
+	break;
+      case GST_BLEND_MODE_LIGHTEN:
+	BLEND_LIGHTEN
+	  ((gint)bgd[bg_pos + 1], (gint)bgd[bg_pos + 2], (gint)bgd[bg_pos + 3],
+	   (gint)fgd[fg_pos + 1], (gint)fgd[fg_pos + 2], (gint)fgd[fg_pos + 3],
+	   alpha, Y, U, V);
+	break;
+      case GST_BLEND_MODE_MULTIPLY:
+	BLEND_MULTIPLY
+	  ((gint)bgd[bg_pos + 1], (gint)bgd[bg_pos + 2], (gint)bgd[bg_pos + 3],
+	   (gint)fgd[fg_pos + 1], (gint)fgd[fg_pos + 2], (gint)fgd[fg_pos + 3],
+	   alpha, Y, U, V);
+	break;
+      case GST_BLEND_MODE_DIFFERENCE:
+	BLEND_DIFFERENCE
+	  ((gint)bgd[bg_pos + 1], (gint)bgd[bg_pos + 2], (gint)bgd[bg_pos + 3],
+	   (gint)fgd[fg_pos + 1], (gint)fgd[fg_pos + 2], (gint)fgd[fg_pos + 3],
+	   alpha, Y, U, V);
+	break;
+      case GST_BLEND_MODE_EXCLUSION:
+	BLEND_EXCLUSION
+	  ((gint)bgd[bg_pos + 1], (gint)bgd[bg_pos + 2], (gint)bgd[bg_pos + 3],
+	   (gint)fgd[fg_pos + 1], (gint)fgd[fg_pos + 2], (gint)fgd[fg_pos + 3],
+	   alpha, Y, U, V);
+	break;
+      case GST_BLEND_MODE_SOFT_LIGHT:
+	BLEND_SOFTLIGHT
+	  ((gint)bgd[bg_pos + 1], (gint)bgd[bg_pos + 2], (gint)bgd[bg_pos + 3],
+	   (gint)fgd[fg_pos + 1], (gint)fgd[fg_pos + 2], (gint)fgd[fg_pos + 3],
+	   alpha, Y, U, V);
+	break;
+      case GST_BLEND_MODE_HARD_LIGHT:
+	BLEND_HARDLIGHT
+	  ((gint)bgd[bg_pos + 1], (gint)bgd[bg_pos + 2], (gint)bgd[bg_pos + 3],
+	   (gint)fgd[fg_pos + 1], (gint)fgd[fg_pos + 2], (gint)fgd[fg_pos + 3],
+	   alpha, Y, U, V);
+	break;
+      case GST_BLEND_MODE_COLOR:
+	BLEND_COLOR
+	  ((gint)bgd[bg_pos + 1], (gint)bgd[bg_pos + 2], (gint)bgd[bg_pos + 3],
+	   (gint)fgd[fg_pos + 1], (gint)fgd[fg_pos + 2], (gint)fgd[fg_pos + 3],
+	   alpha, Y, U, V);
+	break;
+      default:
+	GST_ELEMENT_ERROR (imagemixer, STREAM, WRONG_TYPE, (NULL), (NULL));
+	return;
+      }
+
+      bgd[bg_pos + 0] = 0xff;
+      bgd[bg_pos + 1] = Y;
+      bgd[bg_pos + 2] = U;
+      bgd[bg_pos + 3] = V;
+    }
+  }
+}
+
+void
+gst_imagemixer_loop (GstElement *element)
+{
+  GstImageMixer *imagemixer;
+  GstBuffer *bg = NULL, *outbuf;
+
+  imagemixer = GST_IMAGEMIXER (element);
+
+  if (!GST_PAD_IS_USABLE (imagemixer->bgpad)) {
+    GST_DEBUG_OBJECT (imagemixer, "background pad is not (yet?) usable");
+    return;
+  } else {
+    GstData *data;
+
+    data = gst_pad_pull (GST_PAD (imagemixer->bgpad));
+    if (GST_IS_EVENT (data)) {
+      GstEvent *event = GST_EVENT (data);
+
+      switch (GST_EVENT_TYPE (event)) {
+      case GST_EVENT_EOS:
+	GST_DEBUG_OBJECT (imagemixer, "EOS on background pad %s",
+			  gst_pad_get_name (GST_PAD (imagemixer->bgpad)));
+      default:
+	gst_pad_event_default (GST_PAD (imagemixer->bgpad),
+			       GST_EVENT (data));
+      }
+    }
+
+    if (GST_IS_BUFFER (data)) {
+      bg = GST_BUFFER (data);
+    }
+  }
+
+  if (!GST_PAD_IS_USABLE (imagemixer->fgpad)) {
+    GST_DEBUG_OBJECT (imagemixer, "foreground pad is not (yet?) usable");
+  } else {
+    GstData *data;
+
+    data = gst_pad_pull (GST_PAD (imagemixer->fgpad));
+    if (GST_IS_EVENT (data)) {
+      GstEvent *event = GST_EVENT (data);
+
+      switch (GST_EVENT_TYPE (event)) {
+      case GST_EVENT_EOS:
+	GST_DEBUG_OBJECT (imagemixer, "EOS on foreground pad %s",
+			  gst_pad_get_name (GST_PAD (imagemixer->fgpad)));
+      default:
+	gst_pad_event_default (GST_PAD (imagemixer->fgpad),
+			       GST_EVENT (data));
+      }
+    }
+
+    if (GST_IS_BUFFER (data)) {
+      if (imagemixer->fg_buffer)
+	gst_buffer_unref (imagemixer->fg_buffer);
+
+      imagemixer->fg_buffer = GST_BUFFER (data);
+      gst_buffer_ref (imagemixer->fg_buffer);
+    }
+  }
+
+  if (bg == NULL || imagemixer->fg_buffer == NULL)
+    return;
+
+  if (imagemixer->bg.width != imagemixer->src.width ||
+      imagemixer->bg.height != imagemixer->src.height ||
+      imagemixer->bg.rate != imagemixer->src.rate) {
+    GstCaps *caps;
+    GstStructure *structure;
+    GstPadLinkReturn ret;
+
+    imagemixer->src.width = imagemixer->bg.width;
+    imagemixer->src.height = imagemixer->bg.height;
+    imagemixer->src.rate = imagemixer->bg.rate;
+
+    caps = gst_caps_copy (gst_pad_template_get_caps
+			  (gst_static_pad_template_get
+			   (&gst_imagemixer_src_template)));
+    structure = gst_caps_get_structure (caps, 0);
+    gst_structure_set (structure,
+		       "width", G_TYPE_INT, imagemixer->src.width,
+		       "height", G_TYPE_INT, imagemixer->src.height,
+		       "framerate", G_TYPE_DOUBLE, imagemixer->src.rate,
+		       NULL);
+    ret = gst_pad_try_set_caps (imagemixer->srcpad, caps);
+    gst_caps_free (caps);
+
+    if (GST_PAD_LINK_FAILED (ret)) {
+      GST_ELEMENT_ERROR (imagemixer, CORE, NEGOTIATION, (NULL), (NULL));
+      return;
+    }
+  }
+
+  if (GST_BUFFER_FLAG_IS_SET (bg, GST_BUFFER_READONLY))
+    outbuf = gst_buffer_copy (bg);
+  else
+    outbuf = bg;
+
+  gst_imagemixer_mix (imagemixer, outbuf, imagemixer->fg_buffer);
+
+  gst_pad_push (imagemixer->srcpad, GST_DATA (outbuf));
+}
+
+static gboolean
+plugin_init (GstPlugin * plugin)
+{
+  GST_DEBUG_CATEGORY_INIT (imagemixer_debug, "imagemixer", 0,
+			   "Image mixer");
+
+  return gst_element_register (plugin, "imagemixer", GST_RANK_NONE,
+			       GST_TYPE_IMAGEMIXER);
+}
+
+GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
+		   GST_VERSION_MINOR,
+		   "imagemixer",
+		   "Image mixer",
+		   plugin_init, VERSION, GST_LICENSE,
+		   GST_PACKAGE, GST_ORIGIN)
+
+/*
+ * Local variables:
+ * mode: c
+ * file-style: k&r
+ * c-basic-offset: 2
+ * arch-tag: 83fb9564-c3da-43c4-b1b3-35884e03a1f3
+ * End:
+ */
--- gst/imagemixer/gstimagemixer.h	1970-01-01 01:00:00.000000000 +0100
+++ gst/imagemixer/gstimagemixer.h	2005-02-09 14:21:53.627859207 +0100
@@ -0,0 +1,79 @@
+/* Generic video mixer plugin
+ * Copyright (C) 2004 Wim Taymans <wim@fluendo.com>,
+ *               2005 Gergely Nagy <gergely.nagy@neteyes.hu>
+ *
+ * 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_IMAGEMIXER_H__
+#define __GST_IMAGEMIXER_H__ 1
+
+#include <gst/gst.h>
+
+G_BEGIN_DECLS
+
+#define GST_TYPE_IMAGEMIXER (gst_imagemixer_get_type ())
+#define GST_IMAGEMIXER(obj) \
+  (G_TYPE_CHECK_INSTANCE_CAST (obj, GST_TYPE_IMAGEMIXER, GstImageMixer))
+#define GST_IMAGEMIXER_CLASS(class) \
+  (G_TYPE_CHECK_CLASS_CAST (class, GST_TYPE_IMAGEMIXER, GstImageMixer))
+#define GST_IS_IMAGEMIXER(obj) \
+  (G_TYPE_CHECK_INSTANCE_TYPE (obj, GST_TYPE_IMAGEMIXER)
+#define GST_IS_IMAGEMIXER_CLASS(obj) \
+  (G_TYPE_CHECK_CLASS_TYPE (obj, GST_TYPE_IMAGEMIXER)
+
+typedef struct _GstImageMixer GstImageMixer;
+typedef struct _GstImageMixerClass GstImageMixerClass;
+
+struct _GstImageMixer
+{
+  GstElement element;
+
+  /* pads */
+  GstPad *srcpad;
+  GstPad *bgpad, *fgpad;
+
+  /* properties */
+  gint xpos, ypos;
+  gint mode;
+
+  /* internal */
+  struct
+  {
+    guint width, height;
+    gdouble rate;
+  } bg, fg, src;
+
+  GstBuffer *fg_buffer;
+};
+
+struct _GstImageMixerClass
+{
+  GstElementClass parent_class;
+};
+
+G_END_DECLS
+
+#endif
+
+/*
+ * Local variables:
+ * mode: c
+ * file-style: k&r
+ * c-basic-offset: 2
+ * arch-tag: 1974dc90-3291-4c11-a985-ab6ce6c674d8
+ * End:
+ */
--- gst/imagemixer/Makefile.am	1970-01-01 01:00:00.000000000 +0100
+++ gst/imagemixer/Makefile.am	2005-02-09 14:21:53.626859364 +0100
@@ -0,0 +1,8 @@
+plugin_LTLIBRARIES	= libgstimagemixer.la
+
+libgstimagemixer_la_SOURCES	= gstimagemixer.c gstblend.c
+libgstimagemixer_la_CFLAGS	= ${GST_CFLAGS} ${GST_PLUGINS_CFLAGS}
+libgstimagemixer_la_LIBADD	= ${GST_LIBS} ${GST_PLUGINS_LIBS}
+libgstimagemixer_la_LDFLAGS	= ${GST_PLUGIN_LDFLAGS}
+
+noinst_HEADERS		= gstimagemixer.h gstblend.h
