Index: gst-plugins/configure.ac
===================================================================
--- gst-plugins.orig/configure.ac	2005-06-04 13:22:03.000000000 +0200
+++ gst-plugins/configure.ac	2005-06-05 16:51:41.734233936 +0200
@@ -391,6 +391,7 @@
 	flx \
 	freeze \
 	goom \
+	imagemixer \
 	interleave \
 	law \
 	level \
@@ -2002,6 +2003,7 @@
 gst/freeze/Makefile
 gst/games/Makefile
 gst/goom/Makefile
+gst/imagemixer/Makefile
 gst/interleave/Makefile
 gst/law/Makefile
 gst/level/Makefile
Index: gst-plugins/gst/imagemixer/Makefile.am
===================================================================
--- gst-plugins.orig/gst/imagemixer/Makefile.am	2005-06-05 12:25:09.917537304 +0200
+++ gst-plugins/gst/imagemixer/Makefile.am	2005-06-04 13:25:12.000000000 +0200
@@ -0,0 +1,19 @@
+plugin_LTLIBRARIES = libgstimagemixer.la
+
+libgstimagemixer_la_SOURCES = \
+	mixer.c \
+	mixerpad.c \
+	blend.c \
+	plugin.c
+
+libgstimagemixer_la_CFLAGS = $(GST_CFLAGS)
+libgstimagemixer_la_LIBADD = 
+libgstimagemixer_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
+
+noinst_HEADERS = \
+	mixer.h \
+	mixerpad.h \
+	blend.h
+
+EXTRA_DIST = \
+	README
Index: gst-plugins/gst/imagemixer/README
===================================================================
--- gst-plugins.orig/gst/imagemixer/README	2005-06-05 12:25:09.917537304 +0200
+++ gst-plugins/gst/imagemixer/README	2005-06-04 14:03:23.000000000 +0200
@@ -0,0 +1,23 @@
+Image mixer plugin
+------------------
+
+Based on code from:
+- David Schleef <ds@schleef.org> (pango/textoverlay)
+- Wim Taymans <wim@fluendo.com> (videomixer)
+- Gergely Nagy <algernon@bonehunter.rulez.org> (old imagemixer)
+
+Scenarios:
+- overlay static image (JPEG, alpha-containing PNG) over a video
+- overlay (downscaled) video over a video
+- overlay subtitles (DVD image subtitles or text subtitles) over a video
+
+Design:
+- one sink pad, one source pad; linked formats.
+- a list of (subclass) subpicture_sink_%d pads which are blended over the
+   actual input image from the main sink pad. Overlay image formats are
+   not necessarily the same as the main image format.
+- positioning, alignment, z-order and transparency of the overlays are all
+   configureable.
+
+--
+Ronald S. Bultje <rbultje@ronald.bitfreak.net>
Index: gst-plugins/gst/imagemixer/blend.h
===================================================================
--- gst-plugins.orig/gst/imagemixer/blend.h	2005-06-05 12:25:09.917537304 +0200
+++ gst-plugins/gst/imagemixer/blend.h	2005-06-04 14:24:58.000000000 +0200
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2005 Ronald S. Bultje <rbultje@ronald.bitfreak.net>
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef __GST_IMAGE_MIXER_BLEND_H__
+#define __GST_IMAGE_MIXER_BLEND_H__
+
+G_BEGIN_DECLS
+
+//..
+
+G_END_DECLS
+
+#endif /* __GST_IMAGE_MIXER_BLEND_H__ */
Index: gst-plugins/gst/imagemixer/mixer.c
===================================================================
--- gst-plugins.orig/gst/imagemixer/mixer.c	2005-06-05 12:25:09.917537304 +0200
+++ gst-plugins/gst/imagemixer/mixer.c	2005-06-05 16:17:04.183069720 +0200
@@ -0,0 +1,828 @@
+/*
+ * Copyright (C) 2005 Ronald S. Bultje <rbultje@ronald.bitfreak.net>
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <gst/video/video.h>
+
+#include "mixer.h"
+#include "mixerpad.h"
+
+GST_DEBUG_CATEGORY_EXTERN (imagemixer_debug);
+#define GST_CAT_DEFAULT imagemixer_debug
+
+static GstStaticPadTemplate src_templ = GST_STATIC_PAD_TEMPLATE ("src",
+    GST_PAD_SRC, GST_PAD_ALWAYS,
+    GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("{ AYUV, Y444, I420, YUY2 }")));
+static GstStaticPadTemplate sink_templ = GST_STATIC_PAD_TEMPLATE ("sink",
+    GST_PAD_SINK, GST_PAD_ALWAYS,
+    GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("{ AYUV, Y444, I420, YUY2 }")));
+static GstStaticPadTemplate subpicture_sink_templ = GST_STATIC_PAD_TEMPLATE (
+    "subpicture_sink_%d", GST_PAD_SINK, GST_PAD_REQUEST,
+    GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("{ AYUV, Y444, I420, YUY2 }")));
+
+static GstCaps *gst_image_mixer_fixate		(GstPad     * pad,
+						 const GstCaps * caps);
+static GstCaps *gst_image_mixer_get_caps	(GstPad     * pad);
+static GstPadLinkReturn
+		gst_image_mixer_set_caps	(GstPad     * pad,
+						 const GstCaps * caps);
+static GstPad *	gst_image_mixer_request_pad	(GstElement * element,
+						 GstPadTemplate * templ,
+						 const gchar * name);
+static void	gst_image_mixer_release_pad	(GstElement * element,
+						 GstPad     * pad);
+
+static gboolean	gst_image_mixer_src_event	(GstPad     * pad,
+						 GstEvent   * event);
+static void	gst_image_mixer_loop		(GstElement * element);
+static GstElementStateReturn
+		gst_image_mixer_change_state	(GstElement * element);
+
+/*
+ * Boilerplate code.
+ */
+
+GST_BOILERPLATE (GstImageMixer, gst_image_mixer, GstElement, GST_TYPE_ELEMENT);
+
+static void
+gst_image_mixer_base_init (gpointer data)
+{
+  GstImageMixerClass *klass = data;
+  GstElementClass *eklass = GST_ELEMENT_CLASS (klass);
+  static GstElementDetails imagemixer_details = GST_ELEMENT_DETAILS (
+      "Image mixer",
+      "Filter/Editor/Video",
+      "Generic image mixer",
+      "Ronald S. Bultje <rbultje@ronald.bitfreak.net>");
+
+  gst_element_class_set_details (eklass, &imagemixer_details);
+  gst_element_class_add_pad_template (eklass,
+      gst_static_pad_template_get (&src_templ));
+  gst_element_class_add_pad_template (eklass,
+      gst_static_pad_template_get (&sink_templ));
+  gst_element_class_add_pad_template (eklass,
+      gst_static_pad_template_get (&subpicture_sink_templ));
+}
+
+static void
+gst_image_mixer_class_init (GstImageMixerClass * klass)
+{
+  GstElementClass *eklass = GST_ELEMENT_CLASS (klass);
+
+  eklass->change_state = gst_image_mixer_change_state;
+  eklass->request_new_pad = gst_image_mixer_request_pad;
+  eklass->release_pad = gst_image_mixer_release_pad;
+}
+
+static void
+gst_image_mixer_init (GstImageMixer * mixer)
+{
+  mixer->sink = gst_pad_new_from_template  (
+      gst_static_pad_template_get (&sink_templ), "sink");
+  gst_pad_set_link_function (mixer->sink, gst_image_mixer_set_caps);
+  gst_pad_set_getcaps_function (mixer->sink, gst_image_mixer_get_caps);
+  gst_element_add_pad (GST_ELEMENT (mixer), mixer->sink);
+
+  mixer->src = gst_pad_new_from_template  (
+      gst_static_pad_template_get (&src_templ), "src");
+  gst_pad_set_link_function (mixer->src, gst_image_mixer_set_caps);
+  gst_pad_set_getcaps_function (mixer->src, gst_image_mixer_get_caps);
+  gst_pad_set_event_function (mixer->src, gst_image_mixer_src_event);
+  gst_element_add_pad (GST_ELEMENT (mixer), mixer->src);
+
+  mixer->subpads = NULL;
+  mixer->width = 0;
+  mixer->height = 0;
+  mixer->format = 0;
+  mixer->npad = 0;
+
+  gst_element_set_loop_function (GST_ELEMENT (mixer),
+      gst_image_mixer_loop);
+
+  GST_FLAG_SET (mixer, GST_ELEMENT_EVENT_AWARE);
+}
+
+/*
+ * Request pad management.
+ */
+
+static GstPad *
+gst_image_mixer_request_pad (GstElement * element,
+    GstPadTemplate * templ, const gchar * req_name)
+{
+  GstImageMixer *mixer = GST_IMAGE_MIXER (element);
+  GstElementClass *eklass = GST_ELEMENT_GET_CLASS (element);
+  gchar *name;
+  GstPad *pad;
+
+  g_return_val_if_fail (templ ==
+      gst_element_class_get_pad_template (eklass, "subpicture_sink_%d"), NULL);
+
+  name = g_strdup_printf ("subpicture_sink_%d", mixer->npad);
+  pad = gst_pad_custom_new_from_template (
+      GST_TYPE_IMAGE_MIXER_PAD, templ, name);
+  g_free (name);
+
+  GST_IMAGE_MIXER_PAD (pad)->z_order = mixer->npad;
+  mixer->npad++;
+  mixer->subpads = g_list_append (mixer->subpads, pad);
+
+  gst_pad_set_getcaps_function (pad, gst_image_mixer_get_caps);
+  gst_pad_set_link_function (pad, gst_image_mixer_set_caps);
+  gst_pad_set_fixate_function (pad, gst_image_mixer_fixate);
+  gst_element_add_pad (element, pad);
+
+  return pad;
+}
+
+static void
+gst_image_mixer_release_pad (GstElement * element, GstPad * pad)
+{
+  GstImageMixer *mixer = GST_IMAGE_MIXER (element);
+
+  mixer->subpads = g_list_remove (mixer->subpads, pad);
+  gst_element_remove_pad (element, pad);
+}
+
+/*
+ * Caps negotiation. 
+ */
+
+static GstCaps *
+gst_image_mixer_fixate (GstPad * pad, const GstCaps * caps)
+{
+  GstImageMixer *mixer = GST_IMAGE_MIXER (gst_pad_get_parent (pad));
+  GstCaps *copy;
+  GstStructure *s;
+
+  if (!mixer->width)
+    return NULL;
+
+  /* fixate to our own width */
+  copy = gst_caps_copy (caps);
+  s = gst_caps_get_structure (copy, 0);
+  if (gst_caps_structure_fixate_field_nearest_int (s, "width", mixer->width))
+    return copy;
+  gst_caps_free (copy);
+
+  return NULL;
+}
+
+static GstCaps *
+gst_image_mixer_get_caps (GstPad * pad)
+{
+  GstImageMixer *mixer = GST_IMAGE_MIXER (gst_pad_get_parent (pad));
+
+  if (pad == mixer->sink || pad == mixer->src) {
+    /* main src/sink pad */
+    GstCaps *allowed, *res;
+    GstPad *otherpad = (mixer->src == pad) ? mixer->sink : mixer->src;
+
+    allowed = gst_pad_get_allowed_caps (otherpad);
+    res = gst_caps_intersect (allowed,
+        gst_pad_template_get_caps (gst_pad_get_pad_template (pad)));
+    gst_caps_free (allowed);
+
+    return res;
+  } else {
+    /* sub-picture caps */
+    return gst_caps_copy (
+        gst_pad_template_get_caps (gst_pad_get_pad_template (pad)));
+  }
+}
+
+static GstPadLinkReturn
+gst_image_mixer_set_caps (GstPad * pad, const GstCaps * caps)
+{
+  GstImageMixer *mixer = GST_IMAGE_MIXER (gst_pad_get_parent (pad));
+  GstStructure *s = gst_caps_get_structure (caps, 0);
+
+  if (pad == mixer->src || pad == mixer->sink) {
+    GstPadLinkReturn res;
+    GstPad *otherpad = (pad == mixer->src) ? mixer->sink : mixer->src;
+
+    res = gst_pad_try_set_caps (otherpad, caps);
+    if (GST_PAD_LINK_FAILED (res))
+      return res;
+
+    gst_structure_get_int (s, "width", &mixer->width);
+    gst_structure_get_int (s, "height", &mixer->height);
+    gst_structure_get_fourcc (s, "format", &mixer->format);
+  } else {
+    GstImageMixerPad *ipad = GST_IMAGE_MIXER_PAD (pad);
+
+    gst_structure_get_int (s, "width", &ipad->width);
+    gst_structure_get_int (s, "height", &ipad->height);
+    gst_structure_get_fourcc (s, "format", &ipad->format);
+  }
+
+  return GST_PAD_LINK_OK;
+}
+
+/*
+ * Actual data handling.
+ */
+
+static inline void
+gst_image_mixer_read_a (guint8 * sdata,
+    GstImageMixerPad * ipad, gint x, gint y,
+    gint h_ss, gint v_ss, gfloat * palpha)
+{
+  gint alpha;
+
+  switch (ipad->format) {
+    case GST_MAKE_FOURCC ('A', 'Y', 'U', 'V'):
+      alpha = sdata[4 * (x + y * ipad->width)];
+      if (h_ss > 1) {
+        alpha += sdata[4 * (1 + x + y * ipad->width)];
+      }
+      if (v_ss > 1) {
+        alpha += sdata[4 * (x + (1 + y) * ipad->width)];
+        if (h_ss > 1) {
+          alpha += sdata[4 * (1 + x + (1 + y) * ipad->width)];
+        }
+      }
+      *palpha = ipad->alpha * alpha / (h_ss * v_ss * 255.0);
+      break;
+    default:
+      break;
+  }
+}
+
+static inline gint
+gst_image_mixer_read_y (guint8 * sdata,
+    GstImageMixerPad * ipad, gint x, gint y)
+{
+  gint s_off;
+
+  switch (ipad->format) {
+    case GST_MAKE_FOURCC ('A', 'Y', 'U', 'V'):
+      s_off = 4 * (x + y * ipad->width) + 1;
+      break;
+    case GST_MAKE_FOURCC ('I', '4', '2', '0'):
+      s_off = x + y * ipad->width;
+      break;
+    case GST_MAKE_FOURCC ('Y', '4', '4', '4'):
+      s_off = 3 * (x + y * ipad->width);
+      break;
+    case GST_MAKE_FOURCC ('Y', 'U', 'Y', '2'):
+      s_off = 2  * (x + y * ipad->width);
+      break;
+    default:
+      g_return_val_if_reached (0);
+  }
+
+  return sdata[s_off];
+}
+
+static inline void
+gst_image_mixer_read_u_v (guint8 * sdata,
+    GstImageMixerPad * ipad, gint x, gint y,
+    gint h_ss, gint v_ss, gint * pu, gint * pv)
+{
+  gint u, v;
+
+  switch (ipad->format) {
+    case GST_MAKE_FOURCC ('A', 'Y', 'U', 'V'):
+      u = sdata[4 * (x + y * ipad->width) + 2];
+      v = sdata[4 * (x + y * ipad->width) + 3];
+      if (h_ss > 1) {
+        u += sdata[4 * (1 + x + y * ipad->width) + 2];
+        v += sdata[4 * (1 + x + y * ipad->width) + 3];
+      }
+      if (v_ss > 1) {
+        u += sdata[4 * (x + (1 + y) * ipad->width) + 2];
+        v += sdata[4 * (x + (1 + y) * ipad->width) + 3];
+        if (h_ss > 1) {
+          u += sdata[4 * (1 + x + (1 + y) * ipad->width) + 2];
+          v += sdata[4 * (1 + x + (1 + y) * ipad->width) + 3];
+        }
+      }
+      u /= (h_ss * v_ss);
+      v /= (h_ss * v_ss);
+      break;
+    case GST_MAKE_FOURCC ('I', '4', '2', '0'):
+      u = sdata[ipad->width * ipad->height +
+          (y * ipad->width / 2 + x) / 2];
+      v = sdata[(ipad->width * ipad->height * 5 / 4) +
+          (y * ipad->width / 2 + x) / 2];
+      break;
+    case GST_MAKE_FOURCC ('Y', '4', '4', '4'):
+      u = sdata[3 * (x + y * ipad->width) + 1];
+      v = sdata[3 * (x + y * ipad->width) + 2];
+      if (h_ss > 1) {
+        u += sdata[3 * (1 + x + y * ipad->width) + 1];
+        v += sdata[3 * (1 + x + y * ipad->width) + 2];
+      }
+      if (v_ss > 1) {
+        u += sdata[3 * (x + (1 + y) * ipad->width) + 1];
+        v += sdata[3 * (x + (1 + y) * ipad->width) + 2];
+        if (h_ss > 1) {
+          u += sdata[3 * (1 + x + (1 + y) * ipad->width) + 1];
+          v += sdata[3 * (1 + x + (1 + y) * ipad->width) + 2];
+        }
+      }
+      u /= (h_ss * v_ss);
+      v /= (h_ss * v_ss);
+      break;
+    case GST_MAKE_FOURCC ('Y', 'U', 'Y', '2'):
+      u = sdata[2 * (x + y * ipad->width) + 1];
+      v = sdata[2 * (x + y * ipad->width) + 3];
+      if (v_ss > 1) {
+        u += sdata[2 * (x + (1 + y) * ipad->width) + 1];
+        v += sdata[2 * (x + (1 + y) * ipad->width) + 3];
+        u /= 2;
+        v /= 2;
+      }
+      break;
+    default:
+      g_return_if_reached ();
+  }
+
+  *pu = u;
+  *pv = v;
+}
+
+#define min(a, b) (a > b ? b : a)
+
+static inline void
+gst_image_mixer_do_mix_i420 (GstImageMixer * mixer,
+    GstImageMixerPad * ipad, guint8 * ydata, guint8 * sdata,
+    gint x_off, gint y_off)
+{
+  guint8 *udata = &ydata[mixer->width * mixer->height],
+      *vdata = &udata[mixer->width * mixer->height / 4];
+  gint x, y, py, pu, pv, maxy, maxx;
+  gfloat alpha = ipad->alpha;
+
+  maxy = min (y_off + ipad->height, mixer->height);
+  maxx = min (x_off + ipad->width, mixer->width);
+
+  /* Y plane */
+  for (y = (y_off >= 0) ? y_off : 0; y < maxy; y++) {
+    for (x = (x_off >= 0) ? x_off : 0; x < maxx; x++) {
+      gst_image_mixer_read_a (sdata, ipad, x - x_off, y - y_off, 1, 1, &alpha);
+      if (alpha >= 0.01) {
+        py = gst_image_mixer_read_y (sdata, ipad, x - x_off, y - y_off);
+        ydata[y * mixer->width + x] = alpha * py +
+            (1.0 - alpha) * ydata[y * mixer->width + x];
+      }
+    }
+  }
+
+  /* U/V plane */
+  for (y = (y_off >= 0) ? y_off : 0; y < maxy; y += 2) {
+    for (x = (x_off >= 0) ? x_off : 0; x < maxx; x += 2) {
+      gst_image_mixer_read_a (sdata, ipad, x - x_off, y - y_off, 2, 2, &alpha);
+      if (alpha >= 0.01) {
+        gst_image_mixer_read_u_v (sdata, ipad, x - x_off, y - y_off,
+            2, 2, &pu, &pv);
+        udata[(y * mixer->width / 2 + x) / 2] = alpha * pu +
+            (1.0 - alpha) * udata[(y * mixer->width / 2 + x) / 2];
+        vdata[(y * mixer->width / 2 + x) / 2] = alpha * pv +
+            (1.0 - alpha) * vdata[(y * mixer->width / 2 + x) / 2];
+      }
+    }
+  }
+}
+
+static inline void
+gst_image_mixer_do_mix_yuy2 (GstImageMixer * mixer,
+    GstImageMixerPad * ipad, guint8 * ddata, guint8 * sdata,
+    gint x_off, gint y_off)
+{
+  gint x, y, py, pu, pv, maxx, maxy;
+  gfloat alpha = ipad->alpha;
+
+  maxy = min (y_off + ipad->height, mixer->height);
+  maxx = min (x_off + ipad->width, mixer->width);
+
+  for (y = (y_off >= 0) ? y_off : 0; y < maxy; y++) {
+    for (x = (x_off >= 0) ? x_off : 0; x < maxx; x += 2) {
+      /* Y */
+      gst_image_mixer_read_a (sdata, ipad, x - x_off, y - y_off, 1, 1, &alpha);
+      if (alpha >= 0.01) {
+        py = gst_image_mixer_read_y (sdata, ipad, x - x_off, y - y_off);
+        ddata[2 * (y * mixer->width + x)] = alpha * py +
+            (1.0 - alpha) * ddata[2 * (y * mixer->width + x)];
+      }
+      gst_image_mixer_read_a (sdata, ipad, 1 + x - x_off, y - y_off, 1, 1,
+          &alpha);
+      if (alpha >= 0.01) {
+        py = gst_image_mixer_read_y (sdata, ipad, 1 + x - x_off, y - y_off);
+        ddata[2 * (y * mixer->width + x + 1)] = alpha * py +
+            (1.0 - alpha) * ddata[2 * (y * mixer->width + x + 1)];
+      }
+
+      /* U/V */
+      gst_image_mixer_read_a (sdata, ipad, x - x_off, y - y_off, 2, 1, &alpha);
+      if (alpha >= 0.01) {
+        gst_image_mixer_read_u_v (sdata, ipad, x - x_off, y - y_off,
+            2, 1, &pu, &pv);
+        ddata[2 * (y * mixer->width + x) + 1] = alpha * pu +
+            (1.0 - alpha) * ddata[2 * (y * mixer->width + x) + 1];
+        ddata[2 * (y * mixer->width + x) + 3] = alpha * pv +
+            (1.0 - alpha) * ddata[2 * (y * mixer->width + x) + 3];
+      }
+    }
+  }
+}
+
+static inline void
+gst_image_mixer_do_mix_x444 (GstImageMixer * mixer,
+    GstImageMixerPad * ipad, guint8 * ddata, guint8 * sdata,
+    gint x_off, gint y_off, gint bpp)
+{
+  gint x, y, py, pu, pv, maxx, maxy, h_a = bpp - 3;
+  gfloat alpha = ipad->alpha;
+
+  maxy = min (y_off + ipad->height, mixer->height);
+  maxx = min (x_off + ipad->width, mixer->width);
+
+  /* all together */
+  for (y = (y_off >= 0) ? y_off : 0; y < maxy; y++) {
+    for (x = (x_off >= 0) ? x_off : 0; x < maxx; x++) {
+      gst_image_mixer_read_a (sdata, ipad, x - x_off, y - y_off, 1, 1, &alpha);
+      if (alpha > 0.01) {
+        py = gst_image_mixer_read_y (sdata, ipad, x - x_off, y - y_off);
+        gst_image_mixer_read_u_v (sdata, ipad, x - x_off, y - y_off,
+            1, 1, &pu, &pv);
+
+        ddata[bpp * (y * mixer->width + x) + h_a] = alpha * py +
+            (1.0 - alpha) * ddata[bpp * (y * mixer->width + x) + h_a];
+        ddata[bpp * (y * mixer->width + x) + 1 + h_a] = alpha * pu +
+            (1.0 - alpha) * ddata[bpp * (y * mixer->width + x) + 1 + h_a];
+        ddata[bpp * (y * mixer->width + x) + 2 + h_a] = alpha * pv +
+            (1.0 - alpha) * ddata[bpp * (y * mixer->width + x) + 2 + h_a];
+      }
+    }
+  }
+}
+
+static inline void
+gst_image_mixer_do_mix_y444 (GstImageMixer * mixer,
+    GstImageMixerPad * ipad, guint8 * ddata, guint8 * sdata,
+    gint x_off, gint y_off)
+{
+  gst_image_mixer_do_mix_x444 (mixer, ipad, ddata, sdata, x_off, y_off, 3);
+}
+
+static inline void
+gst_image_mixer_do_mix_ayuv (GstImageMixer * mixer,
+    GstImageMixerPad * ipad, guint8 * ddata, guint8 * sdata,
+    gint x_off, gint y_off)
+{
+  gst_image_mixer_do_mix_x444 (mixer, ipad, ddata, sdata, x_off, y_off, 4);
+}
+
+#undef min
+
+static void
+gst_image_mixer_do_mix (GstImageMixer * mixer,
+    GstImageMixerPad * ipad, GstBuffer * dest, GstBuffer * src)
+{
+  gint x_off, y_off;
+
+  switch (ipad->v_align) {
+    case GST_IMAGE_MIXER_PAD_V_ALIGN_TOP:
+      y_off = ipad->v_offset;
+      break;
+    case GST_IMAGE_MIXER_PAD_V_ALIGN_MIDDLE:
+      y_off = (mixer->height - ipad->height) / 2;
+      break;
+    case GST_IMAGE_MIXER_PAD_V_ALIGN_BOTTOM:
+      y_off = mixer->height - (ipad->height + ipad->v_offset);
+      break;
+    default:
+      g_return_if_reached ();
+      return; /* make compiler happy */
+  }
+
+  switch (ipad->h_align) {
+    case GST_IMAGE_MIXER_PAD_H_ALIGN_LEFT:
+      x_off = ipad->h_offset;
+      break;
+    case GST_IMAGE_MIXER_PAD_H_ALIGN_CENTER:
+      x_off = (mixer->width - ipad->width) / 2;
+      break;
+    case GST_IMAGE_MIXER_PAD_H_ALIGN_RIGHT:
+      x_off = mixer->width - (ipad->width + ipad->h_offset);
+      break;
+    default:
+      g_return_if_reached ();
+      return; /* make compiler happy */
+  }
+
+  GST_DEBUG_OBJECT (mixer, "Blending subpicture of %dx%d ("
+      GST_FOURCC_FORMAT ") at position x=%d, y=%d of %dx%d ("
+      GST_FOURCC_FORMAT ")", ipad->width, ipad->height,
+      GST_FOURCC_ARGS (ipad->format), x_off, y_off,
+      mixer->width, mixer->height, GST_FOURCC_ARGS (mixer->format));
+
+  /* actual blending goes here */
+  switch (mixer->format) {
+    case GST_MAKE_FOURCC ('I', '4', '2', '0'):
+      gst_image_mixer_do_mix_i420 (mixer, ipad,
+          GST_BUFFER_DATA (dest), GST_BUFFER_DATA (src), x_off, y_off);
+      break;
+    case GST_MAKE_FOURCC ('Y', 'U', 'Y', '2'):
+      gst_image_mixer_do_mix_yuy2 (mixer, ipad,
+          GST_BUFFER_DATA (dest), GST_BUFFER_DATA (src), x_off, y_off);
+      break;
+    case GST_MAKE_FOURCC ('A', 'Y', 'U', 'V'):
+      gst_image_mixer_do_mix_ayuv (mixer, ipad,
+          GST_BUFFER_DATA (dest), GST_BUFFER_DATA (src), x_off, y_off);
+      break;
+    case GST_MAKE_FOURCC ('Y', '4', '4', '4'):
+      gst_image_mixer_do_mix_y444 (mixer, ipad,
+          GST_BUFFER_DATA (dest), GST_BUFFER_DATA (src), x_off, y_off);
+      break;
+    default:
+      g_return_if_reached ();
+  }
+}
+
+static gboolean
+is_past_end (GstData * data, guint64 ts)
+{
+  guint64 t, d;
+
+  if (GST_IS_EVENT (data)) {
+    switch (GST_EVENT_TYPE (data)) {
+      case GST_EVENT_EOS:
+        return FALSE;
+      case GST_EVENT_INTERRUPT:
+        return TRUE;
+      case GST_EVENT_FILLER:
+        if (!GST_CLOCK_TIME_IS_VALID (t = GST_EVENT_TIMESTAMP (data)))
+          return TRUE;
+        d = gst_event_filler_get_duration (GST_EVENT (data));
+        break;
+      default:
+        g_assert_not_reached ();
+        return FALSE; /* make compiler happy */
+    }
+  } else {
+    t = GST_BUFFER_TIMESTAMP (data);
+    d = GST_BUFFER_DURATION (data);
+  }
+
+  if (!GST_CLOCK_TIME_IS_VALID (d))
+    return FALSE;
+
+  return (t + d <= ts);
+}
+
+static void
+gst_image_mixer_blend_subpicture (GstImageMixerPad * ipad, GstBuffer ** _buf)
+{
+  GstImageMixer *mixer = GST_IMAGE_MIXER (GST_OBJECT_PARENT (ipad));
+  GstBuffer *buf = *_buf;
+
+  GST_DEBUG_OBJECT (mixer, "Handling subpicture pad %s",
+      GST_OBJECT_NAME (ipad));
+
+  /* get the picture to blend */
+  if (ipad->current_data) {
+    if (is_past_end (ipad->current_data, GST_BUFFER_TIMESTAMP (buf))) {
+      gst_data_unref (ipad->current_data);
+      ipad->current_data = NULL;
+    }
+  }
+
+  /* This state machine was originally in pango/textoverlay. It handles
+   * stuff such as fillers, EOS and buffers (timestamps, durations, etc.)
+   * all well. */
+  while (!ipad->current_data) {
+    GstData *in_data;
+
+    GST_DEBUG_OBJECT (mixer, "Pulling data from pad %s",
+        GST_OBJECT_NAME (ipad));
+
+    in_data = gst_pad_pull (GST_PAD (ipad));
+    if (GST_IS_EVENT (in_data) &&
+        !(GST_EVENT_TYPE (in_data) == GST_EVENT_FILLER &&
+          GST_CLOCK_TIME_IS_VALID (GST_EVENT_TIMESTAMP (in_data)))) {
+      if (GST_EVENT_TYPE (in_data) == GST_EVENT_INTERRUPT ||
+          GST_EVENT_TYPE (in_data) == GST_EVENT_EOS ||
+          GST_EVENT_TYPE (in_data) == GST_EVENT_FILLER) {
+        GST_DEBUG_OBJECT (mixer, "Received empty filler/EOS/interrupt event");
+        ipad->current_data = in_data;
+      } else {
+        GST_DEBUG_OBJECT (mixer, "Received unknown event - dropping");
+        gst_data_unref (in_data);
+      }
+    } else {
+      if (is_past_end (in_data, GST_BUFFER_TIMESTAMP (buf))) {
+        GST_DEBUG_OBJECT (mixer, "Received outdated %s of time %"
+            GST_TIME_FORMAT " and duration %" GST_TIME_FORMAT,
+            GST_IS_EVENT (in_data) ? "filler event" : "buffer",
+            GST_TIME_ARGS (GST_IS_BUFFER (in_data) ?
+                GST_BUFFER_TIMESTAMP (in_data) :
+                GST_EVENT_TIMESTAMP (in_data)),
+            GST_TIME_ARGS (GST_IS_BUFFER (in_data) ?
+                GST_BUFFER_DURATION (in_data) :
+                gst_event_filler_get_duration (GST_EVENT (in_data))));
+
+        gst_data_unref (in_data);
+      } else {
+        GST_DEBUG_OBJECT (mixer, "Using %s of time %" GST_TIME_FORMAT
+            " and duration %" GST_TIME_FORMAT,
+            GST_IS_EVENT (in_data) ? "event" : "buffer",
+            GST_TIME_ARGS (GST_IS_BUFFER (in_data) ?
+                GST_BUFFER_TIMESTAMP (in_data) :
+                GST_EVENT_TIMESTAMP (in_data)),
+            GST_TIME_ARGS (GST_IS_BUFFER (in_data) ?
+                GST_BUFFER_DURATION (in_data) :
+                gst_event_filler_get_duration (GST_EVENT (in_data))));
+
+        ipad->current_data = in_data;
+      }
+    }
+  }
+
+  /* now blend (or skip) */
+  if (GST_IS_BUFFER (ipad->current_data) &&
+      (GST_BUFFER_TIMESTAMP (ipad->current_data) <=
+           GST_BUFFER_TIMESTAMP (buf) ||
+       !GST_BUFFER_TIMESTAMP_IS_VALID (ipad->current_data))) {
+    buf = gst_buffer_copy_on_write (buf);
+    *_buf = buf;
+
+    /* actually merge */
+    GST_DEBUG_OBJECT (mixer, "Drawing subpicture");
+    gst_image_mixer_do_mix (mixer, ipad, buf, GST_BUFFER (ipad->current_data));
+  } else {
+    GST_DEBUG_OBJECT (mixer, "Not drawing subpicture");
+  }
+}
+
+static gboolean
+gst_image_mixer_src_event (GstPad * pad, GstEvent * event)
+{
+  GstImageMixer *mixer = GST_IMAGE_MIXER (gst_pad_get_parent (pad));
+  gboolean res;
+
+  if (!GST_PAD_PEER (mixer->sink)) {
+    gst_event_unref (event);
+    return FALSE;
+  }
+
+  gst_event_ref (event);
+  res = gst_pad_send_event (GST_PAD_PEER (mixer->sink), event);
+
+  /* on seek, flush current data on pads */
+  if (res && GST_EVENT_TYPE (event) == GST_EVENT_SEEK) {
+    GstPad *tpad;
+    GstImageMixerPad *ipad;
+    const GList *item;
+
+    GST_DEBUG_OBJECT (mixer, "Forwarding seek event to all subpic pads");
+    for (item = mixer->subpads; item != NULL; item = item->next) {
+      tpad = item->data;
+      ipad = item->data;
+      if (GST_PAD_PEER (tpad)) {
+        gst_event_ref (event);
+        if (gst_pad_send_event (GST_PAD_PEER (tpad), event)) {
+          if (ipad->current_data) {
+            gst_data_unref (ipad->current_data);
+            ipad->current_data = NULL;
+          }
+        }
+      }
+    }
+  }
+
+  gst_event_unref (event);
+
+  return res;
+}
+
+static gboolean
+gst_image_mixer_handle_sink_event (GstImageMixer * mixer, GstEvent * event)
+{
+  const GList *item;
+
+  GST_DEBUG_OBJECT (mixer, "Handling event of type %d",
+      GST_EVENT_TYPE (event));
+
+  switch (GST_EVENT_TYPE (event)) {
+    case GST_EVENT_INTERRUPT:
+      gst_event_unref (event);
+      return FALSE;
+    case GST_EVENT_EOS:
+      /* EOS subpicture pads */
+      GST_DEBUG_OBJECT (mixer, "Received EOS - EOS'ing subpicture pads");
+
+      for (item = mixer->subpads; item != NULL; item = item->next) {
+        GstPad *pad = item->data;
+        GstImageMixerPad *ipad = item->data;
+
+        GST_DEBUG_OBJECT (mixer, "EOS'ing pad %s", GST_OBJECT_NAME (pad));
+
+        if (ipad->current_data) {
+          gst_data_unref (ipad->current_data);
+          ipad->current_data = NULL;
+        }
+
+        do {
+          GstData *in_data;
+
+          GST_DEBUG_OBJECT (mixer, "pulling data");
+          in_data = gst_pad_pull (pad);
+          if (GST_IS_EVENT (in_data) &&
+              GST_EVENT_TYPE (in_data) == GST_EVENT_EOS) {
+            GST_DEBUG_OBJECT (mixer, "Received EOS");
+            gst_data_unref (in_data);
+            break;
+          }
+          gst_data_unref (in_data);
+        } while (1);
+      }
+      gst_pad_event_default (mixer->sink, event);
+      return FALSE;
+    default:
+      gst_pad_event_default (mixer->sink, event);
+      return TRUE;
+  }
+}
+
+static void
+gst_image_mixer_loop (GstElement * element)
+{
+  GstImageMixer *mixer = GST_IMAGE_MIXER (element);
+  GstBuffer *in_buf = NULL;
+
+  do {
+    GstData *in_data;
+
+    in_data = gst_pad_pull (mixer->sink);
+    GST_DEBUG_OBJECT (mixer, "Pulling data from main pad");
+
+    if (GST_IS_EVENT (in_data)) {
+      if (!gst_image_mixer_handle_sink_event (mixer, GST_EVENT (in_data)))
+        return;
+    } else {
+      in_buf = GST_BUFFER (in_data);
+    }
+  } while (!in_buf);
+
+  GST_DEBUG_OBJECT (mixer, "Processing buffer %p of time %" GST_TIME_FORMAT
+      " and duration %" GST_TIME_FORMAT, in_buf,
+      GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (in_buf)),
+      GST_TIME_ARGS (GST_BUFFER_DURATION (in_buf)));
+  g_list_foreach (mixer->subpads,
+      (GFunc) gst_image_mixer_blend_subpicture, &in_buf);
+
+  GST_DEBUG_OBJECT (mixer, "Processing finished, now pushing buffer %p"
+      " at time %" GST_TIME_FORMAT, in_buf,
+      GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (in_buf)));
+  gst_pad_push (mixer->src, GST_DATA (in_buf));
+}
+
+/*
+ * Clean up on paused->ready.
+ */
+
+static GstElementStateReturn
+gst_image_mixer_change_state (GstElement * element)
+{
+  GstImageMixer *mixer = GST_IMAGE_MIXER (element);
+  const GList *item;
+
+  switch (GST_STATE_TRANSITION (element)) {
+    case GST_STATE_PAUSED_TO_READY:
+      /* clean up pending data in request pads */
+      for (item = mixer->subpads; item != NULL; item = item->next) {
+        GstImageMixerPad *ipad = item->data;
+        if (ipad->current_data) {
+          gst_data_unref (ipad->current_data);
+          ipad->current_data = NULL;
+        }
+      }
+      break;
+    default:
+      break;
+  }
+
+  return GST_CALL_PARENT_WITH_DEFAULT (GST_ELEMENT_CLASS,
+      change_state, (element), GST_STATE_SUCCESS);
+}
Index: gst-plugins/gst/imagemixer/mixer.h
===================================================================
--- gst-plugins.orig/gst/imagemixer/mixer.h	2005-06-05 12:25:09.917537304 +0200
+++ gst-plugins/gst/imagemixer/mixer.h	2005-06-04 16:56:56.000000000 +0200
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2005 Ronald S. Bultje <rbultje@ronald.bitfreak.net>
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef __GST_IMAGE_MIXER_H__
+#define __GST_IMAGE_MIXER_H__
+
+#include <gst/gst.h>
+
+G_BEGIN_DECLS
+
+#define GST_TYPE_IMAGE_MIXER \
+  (gst_image_mixer_get_type ())
+#define GST_IMAGE_MIXER(obj) \
+  (G_TYPE_CHECK_INSTANCE_CAST (obj, GST_TYPE_IMAGE_MIXER, GstImageMixer))
+#define GST_IS_IMAGE_MIXER(obj) \
+  (G_TYPE_CHECK_INSTANCE_TYPE (obj, GST_TYPE_IMAGE_MIXER))
+
+typedef struct _GstImageMixer {
+  GstElement element;
+
+  /* input, output, subpicture pads */
+  GstPad *sink, *src;
+  GList *subpads;
+  guint npad;
+
+  /* main video stream properties */
+  gint width, height;
+  guint32 format; /* fourcc */
+} GstImageMixer;
+
+typedef struct _GstImageMixerClass {
+  GstElementClass element_class;
+} GstImageMixerClass;
+
+GType	gst_image_mixer_get_type	(void);
+
+G_END_DECLS
+
+#endif /* __GST_IMAGE_MIXER_H__ */
Index: gst-plugins/gst/imagemixer/mixerpad.c
===================================================================
--- gst-plugins.orig/gst/imagemixer/mixerpad.c	2005-06-05 12:25:09.917537304 +0200
+++ gst-plugins/gst/imagemixer/mixerpad.c	2005-06-05 10:48:15.000000000 +0200
@@ -0,0 +1,202 @@
+/*
+ * Copyright (C) 2005 Ronald S. Bultje <rbultje@ronald.bitfreak.net>
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "mixerpad.h"
+
+enum {
+  ARG_0,
+  ARG_Z_ORDER,
+#define DEFAULT_Z_ORDER 0
+  ARG_ALPHA,
+#define DEFAULT_ALPHA 1.0
+  ARG_V_ALIGN,
+#define DEFAULT_V_ALIGN GST_IMAGE_MIXER_PAD_V_ALIGN_BOTTOM
+  ARG_H_ALIGN,
+#define DEFAULT_H_ALIGN GST_IMAGE_MIXER_PAD_H_ALIGN_CENTER
+  ARG_V_OFFSET,
+#define DEFAULT_V_OFFSET 6
+  ARG_H_OFFSET
+#define DEFAULT_H_OFFSET 6
+};
+
+GST_DEBUG_CATEGORY_EXTERN (imagemixer_debug);
+#define GST_CAT_DEFAULT imagemixer_debug
+
+G_DEFINE_TYPE (GstImageMixerPad, gst_image_mixer_pad, GST_TYPE_REAL_PAD);
+
+#define GST_IMAGE_MIXER_PAD_TYPE_V_ALIGN \
+  (gst_image_mixer_pad_v_align_get_type ())
+static GType
+gst_image_mixer_pad_v_align_get_type (void)
+{
+  static GType v_align_type = 0;
+
+  if (G_UNLIKELY (v_align_type == 0)) {
+    static GEnumValue v_align_types[] = {
+      {GST_IMAGE_MIXER_PAD_V_ALIGN_TOP, "top", "At the top"},
+      {GST_IMAGE_MIXER_PAD_V_ALIGN_MIDDLE, "middle", "In the middle"},
+      {GST_IMAGE_MIXER_PAD_V_ALIGN_BOTTOM, "bottom", "At the bottom"},
+      {0, NULL, NULL},
+    };
+
+    v_align_type =
+        g_enum_register_static ("GstImageMixerPadVAlign", v_align_types);
+  }
+
+  return v_align_type;
+}
+
+#define GST_IMAGE_MIXER_PAD_TYPE_H_ALIGN \
+  (gst_image_mixer_pad_h_align_get_type ())
+static GType
+gst_image_mixer_pad_h_align_get_type (void)
+{
+  static GType h_align_type = 0;
+
+  if (G_UNLIKELY (h_align_type == 0)) {
+    static GEnumValue h_align_types[] = {
+      {GST_IMAGE_MIXER_PAD_H_ALIGN_LEFT, "left", "At the left"},
+      {GST_IMAGE_MIXER_PAD_H_ALIGN_CENTER, "center", "In the center"},
+      {GST_IMAGE_MIXER_PAD_H_ALIGN_RIGHT, "right", "At the right"},
+      {0, NULL, NULL},
+    };
+
+    h_align_type =
+        g_enum_register_static ("GstImageMixerPadHAlign", h_align_types);
+  }
+
+  return h_align_type;
+}
+
+static void
+gst_image_mixer_pad_set_property (GObject * obj,
+    guint prop_id, const GValue * value, GParamSpec * pspec)
+{
+  GstImageMixerPad *ipad = GST_IMAGE_MIXER_PAD (obj);
+
+  switch (prop_id) {
+    case ARG_Z_ORDER:
+      ipad->z_order = g_value_get_int (value);
+      break;
+    case ARG_ALPHA:
+      ipad->alpha = g_value_get_float (value);
+      break;
+    case ARG_H_ALIGN:
+      ipad->h_align = g_value_get_enum (value);
+      break;
+    case ARG_V_ALIGN:
+      ipad->v_align = g_value_get_enum (value);
+      break;
+    case ARG_H_OFFSET:
+      ipad->h_offset = g_value_get_int (value);
+      break;
+    case ARG_V_OFFSET:
+      ipad->v_offset = g_value_get_int (value);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
+      break;
+  }
+}
+
+static void
+gst_image_mixer_pad_get_property (GObject * obj,
+    guint prop_id, GValue * value, GParamSpec * pspec)
+{
+  GstImageMixerPad *ipad = GST_IMAGE_MIXER_PAD (obj);
+
+  switch (prop_id) {
+    case ARG_Z_ORDER:
+      g_value_set_int (value, ipad->z_order);
+      break;
+    case ARG_ALPHA:
+      g_value_set_float (value, ipad->alpha);
+      break;
+    case ARG_H_ALIGN:
+      g_value_set_enum (value, ipad->h_align);
+      break;
+    case ARG_V_ALIGN:
+      g_value_set_enum (value, ipad->v_align);
+      break;
+    case ARG_H_OFFSET:
+      g_value_set_int (value, ipad->h_offset);
+      break;
+    case ARG_V_OFFSET:
+      g_value_set_int (value, ipad->v_offset);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
+      break;
+  }
+}
+
+static void
+gst_image_mixer_pad_class_init (GstImageMixerPadClass * klass)
+{
+  GObjectClass *oklass = G_OBJECT_CLASS (klass);
+
+  oklass->set_property = gst_image_mixer_pad_set_property;
+  oklass->get_property = gst_image_mixer_pad_get_property;
+
+  g_object_class_install_property (oklass, ARG_Z_ORDER,
+      g_param_spec_int ("z-order", "Z-Order",
+          "Z Order of the sub-picture (0 = top, 10000 = bottom)",
+          0, 10000, DEFAULT_Z_ORDER, G_PARAM_READWRITE));
+  g_object_class_install_property (oklass, ARG_ALPHA,
+      g_param_spec_float ("alpha", "Alpha",
+          "Alpha of the sub-picture (0.0 = transparent, 1.0 = fully visible)",
+          0.0, 1.0, DEFAULT_ALPHA, G_PARAM_READWRITE));
+  g_object_class_install_property (oklass, ARG_V_ALIGN,
+      g_param_spec_enum ("v-align", "Vert. alignment",
+          "Vertical alignment of the sub-picture",
+          GST_IMAGE_MIXER_PAD_TYPE_V_ALIGN,
+          DEFAULT_V_ALIGN, G_PARAM_READWRITE));
+  g_object_class_install_property (oklass, ARG_H_ALIGN,
+      g_param_spec_enum ("h-align", "Hor. alignment",
+          "Horizontal alignment of the sub-picture",
+          GST_IMAGE_MIXER_PAD_TYPE_H_ALIGN,
+          DEFAULT_H_ALIGN, G_PARAM_READWRITE));
+  g_object_class_install_property (oklass, ARG_V_OFFSET,
+      g_param_spec_int ("v-offset", "Vert. offset",
+          "Vertical offset of the sub-picture (respective to alignment)",
+          G_MININT, G_MAXINT, DEFAULT_V_OFFSET, G_PARAM_READWRITE));
+  g_object_class_install_property (oklass, ARG_H_OFFSET,
+      g_param_spec_int ("h-offset", "Hor. offset",
+          "Horizontal offset of the sub-picture (respective to alignment)",
+          G_MININT, G_MAXINT, DEFAULT_H_OFFSET, G_PARAM_READWRITE));
+}
+
+static void
+gst_image_mixer_pad_init (GstImageMixerPad * ipad)
+{
+  ipad->z_order = DEFAULT_Z_ORDER;
+  ipad->h_offset = DEFAULT_H_OFFSET;
+  ipad->v_offset = DEFAULT_V_OFFSET;
+  ipad->h_align = DEFAULT_H_ALIGN;
+  ipad->v_align = DEFAULT_V_ALIGN;
+  ipad->alpha = DEFAULT_ALPHA;
+
+  ipad->width = 0;
+  ipad->height = 0;
+  ipad->format = 0;
+  ipad->current_data = NULL;
+}
Index: gst-plugins/gst/imagemixer/mixerpad.h
===================================================================
--- gst-plugins.orig/gst/imagemixer/mixerpad.h	2005-06-05 12:25:09.917537304 +0200
+++ gst-plugins/gst/imagemixer/mixerpad.h	2005-06-04 18:09:03.000000000 +0200
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2005 Ronald S. Bultje <rbultje@ronald.bitfreak.net>
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef __GST_IMAGE_MIXER_PAD_H__
+#define __GST_IMAGE_MIXER_PAD_H__
+
+#include <gst/gst.h>
+
+G_BEGIN_DECLS
+
+#define GST_TYPE_IMAGE_MIXER_PAD \
+  (gst_image_mixer_pad_get_type ())
+#define GST_IMAGE_MIXER_PAD(obj) \
+  (G_TYPE_CHECK_INSTANCE_CAST (obj, GST_TYPE_IMAGE_MIXER_PAD, GstImageMixerPad))
+#define GST_IS_IMAGE_MIXER_PAD(obj) \
+  (G_TYPE_CHECK_INSTANCE_TYPE (obj, GST_TYPE_IMAGE_MIXER_PAD))
+
+typedef enum {
+  GST_IMAGE_MIXER_PAD_V_ALIGN_TOP,
+  GST_IMAGE_MIXER_PAD_V_ALIGN_MIDDLE,
+  GST_IMAGE_MIXER_PAD_V_ALIGN_BOTTOM
+} GstImageMixerPadVAlign;
+
+typedef enum {
+  GST_IMAGE_MIXER_PAD_H_ALIGN_LEFT,
+  GST_IMAGE_MIXER_PAD_H_ALIGN_CENTER,
+  GST_IMAGE_MIXER_PAD_H_ALIGN_RIGHT
+} GstImageMixerPadHAlign;
+
+typedef struct _GstImageMixerPad {
+  GstRealPad real_pad;
+
+  /* size */
+  gint width, height;
+  guint32 format; /* fourcc */
+
+  /* cache current data (filler, interrupt, eos or buffer) */
+  GstData *current_data;
+
+  /* properties */
+  gint z_order;
+  gfloat alpha;
+  gint v_offset, h_offset;
+  gint v_align, h_align;
+} GstImageMixerPad;
+
+typedef struct _GstImageMixerPadClass {
+  GstRealPadClass real_pad_class;
+} GstImageMixerPadClass;
+
+GType	gst_image_mixer_pad_get_type	(void);
+
+G_END_DECLS
+
+#endif /* __GST_IMAGE_MIXER_PAD_H__ */
Index: gst-plugins/gst/imagemixer/plugin.c
===================================================================
--- gst-plugins.orig/gst/imagemixer/plugin.c	2005-06-05 12:25:09.917537304 +0200
+++ gst-plugins/gst/imagemixer/plugin.c	2005-06-04 18:47:45.000000000 +0200
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2005 Ronald S. Bultje <rbultje@ronald.bitfreak.net>
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "mixer.h"
+
+GST_DEBUG_CATEGORY (imagemixer_debug);
+
+static gboolean
+plugin_init (GstPlugin * plugin)
+{
+  if (!gst_element_register (plugin, "imagemixer", GST_RANK_NONE,
+          GST_TYPE_IMAGE_MIXER))
+    return FALSE;
+
+  GST_DEBUG_CATEGORY_INIT (imagemixer_debug, "imagemixer", 0, "Image mixer");
+
+  return TRUE;
+}
+
+GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
+    GST_VERSION_MINOR,
+    "imagemixer",
+    "Image mixing plugin",
+    plugin_init, VERSION, "LGPL", GST_PACKAGE, GST_ORIGIN)
