? chain-render.sh
? gsttextrender.c
? gsttextrender.h
Index: Makefile.am
===================================================================
RCS file: /cvs/gstreamer/gst-plugins/ext/pango/Makefile.am,v
retrieving revision 1.3
diff -u -r1.3 Makefile.am
--- Makefile.am	9 Aug 2003 23:56:38 -0000	1.3
+++ Makefile.am	5 Jun 2005 14:44:01 -0000
@@ -4,14 +4,14 @@
 
 plugin_LTLIBRARIES = libgsttimeoverlay.la libgsttextoverlay.la
 
-noinst_HEADERS = gsttimeoverlay.h gsttextoverlay.h
+noinst_HEADERS = gsttimeoverlay.h gsttextoverlay.h gsttextrender.h
 
 libgsttimeoverlay_la_SOURCES = gsttimeoverlay.c
 libgsttimeoverlay_la_CFLAGS = $(GST_CFLAGS) $(PANGO_CFLAGS) -I$(top_srcdir)/gst/videofilter
 libgsttimeoverlay_la_LIBADD =
 libgsttimeoverlay_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) $(PANGO_LIBS) -lm
 
-libgsttextoverlay_la_SOURCES = gsttextoverlay.c
+libgsttextoverlay_la_SOURCES = gsttextoverlay.c gsttextrender.c
 libgsttextoverlay_la_CFLAGS = $(GST_CFLAGS) $(PANGO_CFLAGS)
 libgsttextoverlay_la_LIBADD =
 libgsttextoverlay_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) $(PANGO_LIBS)
Index: gsttextoverlay.c
===================================================================
RCS file: /cvs/gstreamer/gst-plugins/ext/pango/gsttextoverlay.c,v
retrieving revision 1.16.2.5
diff -u -r1.16.2.5 gsttextoverlay.c
--- gsttextoverlay.c	5 May 2005 19:23:15 -0000	1.16.2.5
+++ gsttextoverlay.c	5 Jun 2005 14:44:02 -0000
@@ -22,9 +22,10 @@
 #include <config.h>
 #endif
 #include <gst/gst.h>
+#include "gsttextrender.h"
 #include "gsttextoverlay.h"
 
-GST_DEBUG_CATEGORY_STATIC (pango_debug);
+GST_DEBUG_CATEGORY (pango_debug);
 #define GST_CAT_DEFAULT pango_debug
 
 static GstElementDetails textoverlay_details = {
@@ -810,7 +811,9 @@
 plugin_init (GstPlugin * plugin)
 {
   if (!gst_element_register (plugin, "textoverlay", GST_RANK_NONE,
-          GST_TYPE_TEXTOVERLAY))
+          GST_TYPE_TEXTOVERLAY) ||
+      !gst_element_register (plugin, "textrender", GST_RANK_NONE,
+          GST_TYPE_TEXT_RENDER))
     return FALSE;
 
   /*texttestsrc_plugin_init(module, plugin); */
--- /dev/null	2005-06-05 12:25:09.917537304 +0200
+++ gsttextrender.c	2005-06-05 16:25:30.615080352 +0200
@@ -0,0 +1,419 @@
+/* GStreamer
+ * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
+ * Copyright (C) <2003> 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., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+#include <gst/gst.h>
+#include "gsttextrender.h"
+
+GST_DEBUG_CATEGORY_EXTERN (pango_debug);
+#define GST_CAT_DEFAULT pango_debug
+
+static GstElementDetails text_render_details = {
+  "Text Render",
+  "Filter/Editor/Video",
+  "Renders a text string to a image bitmap",
+  "David Schleef <ds@schleef.org>, "
+  "Ronald S. Bultje <rbultje@ronald.bitfreak.net>"
+};
+
+enum
+{
+  ARG_0,
+  ARG_FONT_DESC
+};
+
+
+static GstStaticPadTemplate src_template_factory =
+GST_STATIC_PAD_TEMPLATE ("src",
+    GST_PAD_SRC,
+    GST_PAD_ALWAYS,
+    GST_STATIC_CAPS ("video/x-raw-yuv, "
+        "format = (fourcc) AYUV, "
+        "width = (int) [ 1, MAX ], "
+        "height = (int) [ 1, MAX ], framerate = (double) 1")
+    );
+
+static GstStaticPadTemplate sink_template_factory =
+    GST_STATIC_PAD_TEMPLATE ("sink",
+    GST_PAD_SINK,
+    GST_PAD_ALWAYS,
+    GST_STATIC_CAPS ("text/x-pango-markup; text/plain")
+    );
+
+static void gst_text_render_base_init (gpointer g_class);
+static void gst_text_render_class_init (GstTextRenderClass * klass);
+static void gst_text_render_init (GstTextRender * overlay);
+static void gst_text_render_set_property (GObject * object,
+    guint prop_id, const GValue * value, GParamSpec * pspec);
+static void gst_text_render_get_property (GObject * object,
+    guint prop_id, GValue * value, GParamSpec * pspec);
+static void gst_text_render_finalize (GObject * object);
+
+static GstElementClass *parent_class = NULL;
+
+/*static guint gst_text_render_signals[LAST_SIGNAL] = { 0 }; */
+
+GType
+gst_text_render_get_type (void)
+{
+  static GType text_render_type = 0;
+
+  if (!text_render_type) {
+    static const GTypeInfo text_render_info = {
+      sizeof (GstTextRenderClass),
+      gst_text_render_base_init,
+      NULL,
+      (GClassInitFunc) gst_text_render_class_init,
+      NULL,
+      NULL,
+      sizeof (GstTextRender),
+      0,
+      (GInstanceInitFunc) gst_text_render_init,
+    };
+
+    text_render_type =
+        g_type_register_static (GST_TYPE_ELEMENT, "GstTextRender",
+        &text_render_info, 0);
+  }
+  return text_render_type;
+}
+
+static void
+gst_text_render_base_init (gpointer g_class)
+{
+  GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
+
+  gst_element_class_add_pad_template (element_class,
+      gst_static_pad_template_get (&src_template_factory));
+  gst_element_class_add_pad_template (element_class,
+      gst_static_pad_template_get (&sink_template_factory));
+
+  gst_element_class_set_details (element_class, &text_render_details);
+}
+
+static void
+gst_text_render_class_init (GstTextRenderClass * klass)
+{
+  GObjectClass *gobject_class;
+  GstElementClass *gstelement_class;
+
+  gobject_class = (GObjectClass *) klass;
+  gstelement_class = (GstElementClass *) klass;
+
+  parent_class = g_type_class_peek_parent (klass);
+
+  gobject_class->finalize = gst_text_render_finalize;
+  gobject_class->set_property = gst_text_render_set_property;
+  gobject_class->get_property = gst_text_render_get_property;
+
+  klass->pango_context = pango_ft2_get_context (72, 72);
+  g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_FONT_DESC,
+      g_param_spec_string ("font-desc", "font description",
+          "Pango font description of font "
+          "to be used for rendering. "
+          "See documentation of "
+          "pango_font_description_from_string"
+          " for syntax.", "", G_PARAM_WRITABLE));
+}
+
+
+static void
+resize_bitmap (GstTextRender * overlay, int width, int height)
+{
+  FT_Bitmap *bitmap = &overlay->bitmap;
+  int pitch = (width | 3) + 1;
+  int size = pitch * height;
+
+  /* no need to keep reallocating; just keep the maximum size so far */
+  if (size <= overlay->bitmap_buffer_size) {
+    bitmap->rows = height;
+    bitmap->width = width;
+    bitmap->pitch = pitch;
+    memset (bitmap->buffer, 0, overlay->bitmap_buffer_size);
+    return;
+  }
+  if (!bitmap->buffer) {
+    /* initialize */
+    bitmap->pixel_mode = ft_pixel_mode_grays;
+    bitmap->num_grays = 256;
+  }
+  if (bitmap->buffer)
+    bitmap->buffer = g_realloc (bitmap->buffer, size);
+  else
+    bitmap->buffer = g_malloc (size);
+  bitmap->rows = height;
+  bitmap->width = width;
+  bitmap->pitch = pitch;
+  memset (bitmap->buffer, 0, size);
+  overlay->bitmap_buffer_size = size;
+}
+
+static void
+render_text (GstTextRender * overlay)
+{
+  PangoRectangle ink_rect, logical_rect;
+
+  pango_layout_get_pixel_extents (overlay->layout, &ink_rect, &logical_rect);
+  resize_bitmap (overlay, ink_rect.width, ink_rect.height + ink_rect.y);
+  pango_ft2_render_layout (&overlay->bitmap, overlay->layout, -ink_rect.x, 0);
+  overlay->baseline_y = ink_rect.y;
+}
+
+static GstPadLinkReturn
+gst_text_render_link (GstPad * pad, const GstCaps * caps)
+{
+  GstTextRender *overlay = GST_TEXT_RENDER (gst_pad_get_parent (pad));
+  GstStructure *structure;
+
+  structure = gst_caps_get_structure (caps, 0);
+  overlay->width = overlay->height = 0;
+  gst_structure_get_int (structure, "width", &overlay->width);
+  gst_structure_get_int (structure, "height", &overlay->height);
+
+  return GST_PAD_LINK_OK;
+}
+
+static GstCaps *
+gst_text_render_fixate (GstPad * pad, const GstCaps * caps)
+{
+  GstTextRender *overlay = GST_TEXT_RENDER (gst_pad_get_parent (pad));
+  GstCaps *copy = gst_caps_copy (caps);
+  GstStructure *s = gst_caps_get_structure (copy, 0);
+
+  if (gst_caps_structure_fixate_field_nearest_int (s, "width",
+          overlay->bitmap.width) ||
+      gst_caps_structure_fixate_field_nearest_int (s, "height",
+          overlay->bitmap.rows))
+    return copy;
+
+  gst_caps_free (copy);
+
+  return NULL;
+}
+
+static void
+gst_text_overlay_blit_yuv420 (GstTextRender * overlay, FT_Bitmap * bitmap,
+    guchar * pixbuf, int x0, int y0)
+{
+  int y;                        /* text bitmap coordinates */
+  int x1, y1;                   /* video buffer coordinates */
+  int rowinc, bit_rowinc;
+  guchar *p, *bitp;
+  int video_width = overlay->width, video_height = overlay->height;
+  int bitmap_x0 = 0;            //x0 < 1 ? -(x0 - 1) : 1;       /* 1 pixel border */
+  int bitmap_y0 = y0 < 1 ? -(y0 - 1) : 1;       /* 1 pixel border */
+  int bitmap_width = bitmap->width - bitmap_x0;
+  int bitmap_height = bitmap->rows - bitmap_y0;
+  int skip_y, skip_x;
+  guchar v;
+
+  if (x0 + bitmap_x0 + bitmap_width > video_width - 1)  /* 1 pixel border */
+    bitmap_width -= x0 + bitmap_x0 + bitmap_width - video_width + 1;
+  if (y0 + bitmap_y0 + bitmap_height > video_height - 1)        /* 1 pixel border */
+    bitmap_height -= y0 + bitmap_y0 + bitmap_height - video_height + 1;
+
+  rowinc = video_width - bitmap_width;
+  bit_rowinc = bitmap->pitch - bitmap_width;
+
+  y1 = y0 + bitmap_y0;
+  x1 = x0 + bitmap_x0;
+  p = pixbuf + 1 + (video_width * y1 + x1) * 4;
+  bitp = bitmap->buffer + bitmap->pitch * bitmap_y0 + bitmap_x0;
+  for (y = bitmap_y0; y < bitmap_y0 + bitmap_height; y++) {
+    int n;
+
+    for (n = bitmap_width; n > 0; --n) {
+      v = *bitp;
+      if (v) {
+        p[-4] = CLAMP (p[-4] - v, 0, 255);
+        p[4] = CLAMP (p[4] - v, 0, 255);
+        p[-4 * video_width] = CLAMP (p[-4 * video_width] - v, 0, 255);
+        p[4 * video_width] = CLAMP (p[4 * video_width] - v, 0, 255);
+      }
+      p += 4;
+      bitp++;
+    }
+    p += rowinc * 4;
+    bitp += bit_rowinc;
+  }
+
+  y1 = y0 + bitmap_y0;
+  x1 = x0 + bitmap_x0;
+  bitp = bitmap->buffer + bitmap->pitch * bitmap_y0 + bitmap_x0;
+  p = pixbuf + (video_width * y1 + x1) * 4;
+  skip_y = 0;
+  skip_x = 0;
+
+  for (y = bitmap_y0; y < bitmap_y0 + bitmap_height; y++) {
+    int n;
+
+    x1 = x0 + bitmap_x0;
+    skip_x = 0;
+    for (n = bitmap_width; n > 0; --n) {
+      v = *bitp;
+      if (v) {
+        p[0] = v;
+        p[1] = 255;
+        p[2] = p[3] = 0x80;
+      }
+      p += 4;
+      bitp++;
+    }
+    /*if (!skip_x && !skip_y) u_p--; */
+    p += rowinc * 4;
+    bitp += bit_rowinc;
+  }
+}
+
+
+static void
+gst_text_render_chain (GstPad * pad, GstData * _data)
+{
+  GstBuffer *buf = GST_BUFFER (_data), *out;
+  GstTextRender *overlay = GST_TEXT_RENDER (gst_pad_get_parent (pad));
+  guint size = GST_BUFFER_SIZE (buf);
+  guint8 *data = GST_BUFFER_DATA (buf);
+
+  /* somehow pango barfs over "\0" buffers... */
+  while (size > 0 &&
+      (data[size - 1] == '\r' ||
+          data[size - 1] == '\n' || data[size - 1] == '\0')) {
+    size--;
+  }
+
+  /* render text */
+  GST_DEBUG ("rendering '%*s'", size, data);
+  pango_layout_set_markup (overlay->layout, (gchar *) data, size);
+  render_text (overlay);
+
+  if (GST_PAD_LINK_FAILED (gst_pad_renegotiate (overlay->srcpad))) {
+    GST_ELEMENT_ERROR (overlay, CORE, NEGOTIATION, (NULL), (NULL));
+    return;
+  }
+
+  /* put in a buffer */
+  out = gst_buffer_new_and_alloc (overlay->width * overlay->height * 4);
+  gst_buffer_stamp (out, buf);
+  data = GST_BUFFER_DATA (out);
+gint n;
+for (n = 0; n < overlay->width * overlay->height; n++){
+data[n*4] = 0;
+data[n*4+1] = 0;
+data[n*4+2] = data[n*4+3] = 128;
+}
+  if (overlay->bitmap.buffer) {
+    gst_text_overlay_blit_yuv420 (overlay, &overlay->bitmap, data, 0, 0);
+  }
+  gst_data_unref (_data);
+
+  gst_pad_push (overlay->srcpad, GST_DATA (out));
+}
+
+static void
+gst_text_render_finalize (GObject * object)
+{
+  GstTextRender *overlay = GST_TEXT_RENDER (object);
+
+  if (overlay->layout) {
+    g_object_unref (overlay->layout);
+    overlay->layout = NULL;
+  }
+  if (overlay->bitmap.buffer) {
+    g_free (overlay->bitmap.buffer);
+    overlay->bitmap.buffer = NULL;
+  }
+
+  G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static void
+gst_text_render_init (GstTextRender * overlay)
+{
+  /* sink */
+  overlay->sinkpad =
+      gst_pad_new_from_template (gst_static_pad_template_get
+      (&sink_template_factory), "sink");
+  gst_pad_set_chain_function (overlay->sinkpad, gst_text_render_chain);
+  gst_element_add_pad (GST_ELEMENT (overlay), overlay->sinkpad);
+
+  /* source */
+  overlay->srcpad =
+      gst_pad_new_from_template (gst_static_pad_template_get
+      (&src_template_factory), "src");
+  gst_pad_set_link_function (overlay->srcpad, gst_text_render_link);
+  gst_pad_set_fixate_function (overlay->srcpad, gst_text_render_fixate);
+  gst_element_add_pad (GST_ELEMENT (overlay), overlay->srcpad);
+
+  overlay->layout =
+      pango_layout_new (GST_TEXT_RENDER_GET_CLASS (overlay)->pango_context);
+  memset (&overlay->bitmap, 0, sizeof (overlay->bitmap));
+}
+
+
+static void
+gst_text_render_set_property (GObject * object, guint prop_id,
+    const GValue * value, GParamSpec * pspec)
+{
+  GstTextRender *overlay;
+
+  /* it's not null if we got it, but it might not be ours */
+  g_return_if_fail (GST_IS_TEXT_RENDER (object));
+  overlay = GST_TEXT_RENDER (object);
+
+  switch (prop_id) {
+    case ARG_FONT_DESC:
+    {
+      PangoFontDescription *desc;
+
+      desc = pango_font_description_from_string (g_value_get_string (value));
+      if (desc) {
+        GST_LOG ("font description set: %s", g_value_get_string (value));
+        pango_layout_set_font_description (overlay->layout, desc);
+        pango_font_description_free (desc);
+        render_text (overlay);
+      } else
+        GST_WARNING ("font description parse failed: %s",
+            g_value_get_string (value));
+      break;
+    }
+
+    default:
+      break;
+  }
+}
+
+static void
+gst_text_render_get_property (GObject * object, guint prop_id, GValue * value,
+    GParamSpec * pspec)
+{
+  GstTextRender *overlay;
+
+  /* it's not null if we got it, but it might not be ours */
+  g_return_if_fail (GST_IS_TEXT_RENDER (object));
+  overlay = GST_TEXT_RENDER (object);
+
+  switch (prop_id) {
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+  }
+}
--- /dev/null	2005-06-05 12:25:09.917537304 +0200
+++ gsttextrender.h	2005-06-05 15:15:34.281020096 +0200
@@ -0,0 +1,47 @@
+/* -*- Mode: C; c-file-style: "stroustrup" -*- */
+#ifndef __GST_TEXT_RENDER_H__
+#define __GST_TEXT_RENDER_H__
+
+#include <gst/gst.h>
+#include <pango/pangoft2.h>
+
+G_BEGIN_DECLS
+
+#define GST_TYPE_TEXT_RENDER           (gst_text_render_get_type())
+#define GST_TEXT_RENDER(obj)           (G_TYPE_CHECK_INSTANCE_CAST((obj),\
+                                        GST_TYPE_TEXT_RENDER, GstTextRender))
+#define GST_TEXT_RENDER_CLASS(klass)   (G_TYPE_CHECK_CLASS_CAST((klass),\
+                                        GST_TYPE_ULAW, GstTextRender))
+#define GST_TEXT_RENDER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj),\
+                                        GST_TYPE_TEXT_RENDER, GstTextRenderClass))
+#define GST_IS_TEXT_RENDER(obj)        (G_TYPE_CHECK_INSTANCE_TYPE((obj),\
+                                        GST_TYPE_TEXT_RENDER))
+#define GST_IS_TEXT_RENDER_CLASS(obj)  (G_TYPE_CHECK_CLASS_TYPE((klass),\
+                                        GST_TYPE_TEXT_RENDER))
+
+typedef struct _GstTextRender      GstTextRender;
+typedef struct _GstTextRenderClass GstTextRenderClass;
+
+struct _GstTextRender {
+    GstElement            element;
+
+    GstPad               *sinkpad, *srcpad;
+    gint                  width;
+    gint                  height;
+    PangoLayout          *layout;
+    FT_Bitmap             bitmap;
+    gint                  bitmap_buffer_size;
+    gint                  baseline_y;
+};
+
+struct _GstTextRenderClass {
+    GstElementClass parent_class;
+
+    PangoContext *pango_context;
+};
+
+GType gst_text_render_get_type(void) G_GNUC_CONST;
+
+G_END_DECLS
+
+#endif /* __GST_TEXT_RENDER_H */
