Index: ext/pango/gsttextoverlay.c
===================================================================
RCS file: /cvs/gstreamer/gst-plugins-base/ext/pango/gsttextoverlay.c,v
retrieving revision 1.58
diff -u -r1.58 gsttextoverlay.c
--- ext/pango/gsttextoverlay.c	2 Jun 2008 18:37:02 -0000	1.58
+++ ext/pango/gsttextoverlay.c	20 Jun 2008 08:55:40 -0000
@@ -3,6 +3,7 @@
  * Copyright (C) <2003> David Schleef <ds@schleef.org>
  * Copyright (C) <2006> Julien Moutte <julien@moutte.net>
  * Copyright (C) <2006-2008> Tim-Philipp Müller <tim centricular net>
+ * Copyright (C) <2008> Young-Ho Cha <ganadist@gmail.com>
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Library General Public
@@ -84,6 +85,7 @@
 #include "gsttimeoverlay.h"
 #include "gstclockoverlay.h"
 #include "gsttextrender.h"
+#include <string.h>
 
 /* FIXME:
  *  - use proper strides and offset for I420
@@ -117,9 +119,14 @@
 #define DEFAULT_PROP_SILENT	FALSE
 #define DEFAULT_PROP_LINE_ALIGNMENT GST_TEXT_OVERLAY_LINE_ALIGN_CENTER
 #define DEFAULT_PROP_WAIT_TEXT	TRUE
+#define DEFAULT_PROP_AUTO_ADJUST_SIZE TRUE
+#define DEFAULT_PROP_VERTICAL_RENDER  FALSE
 
 /* make a property of me */
-#define DEFAULT_SHADING_VALUE    -80
+#define DEFAULT_SHADING_VALUE  0.3
+
+#define MINIMUM_OUTLINE_OFFSET 1.0
+#define DEFAULT_SCALE_BASIS    640
 
 enum
 {
@@ -139,6 +146,8 @@
   PROP_SILENT,
   PROP_LINE_ALIGNMENT,
   PROP_WAIT_TEXT,
+  PROP_AUTO_ADJUST_SIZE,
+  PROP_VERTICAL_RENDER,
   PROP_LAST
 };
 
@@ -269,12 +278,15 @@
     GstPad * peer);
 static void gst_text_overlay_text_pad_unlink (GstPad * pad);
 static void gst_text_overlay_pop_text (GstTextOverlay * overlay);
+static void gst_text_overlay_update_render_mode (GstTextOverlay * overlay);
 
 static void gst_text_overlay_finalize (GObject * object);
 static void gst_text_overlay_set_property (GObject * object, guint prop_id,
     const GValue * value, GParamSpec * pspec);
 static void gst_text_overlay_get_property (GObject * object, guint prop_id,
     GValue * value, GParamSpec * pspec);
+static void gst_text_overlay_adjust_values_with_fontdesc (GstTextOverlay *
+    overlay, PangoFontDescription * desc);
 
 GST_BOILERPLATE (GstTextOverlay, gst_text_overlay, GstElement, GST_TYPE_ELEMENT)
 
@@ -308,6 +320,7 @@
 {
   GObjectClass *gobject_class;
   GstElementClass *gstelement_class;
+  PangoFontMap *fontmap;
 
   gobject_class = (GObjectClass *) klass;
   gstelement_class = (GstElementClass *) klass;
@@ -320,7 +333,9 @@
       GST_DEBUG_FUNCPTR (gst_text_overlay_change_state);
 
   klass->get_text = gst_text_overlay_get_text;
-  klass->pango_context = pango_ft2_get_context (72, 72);
+  fontmap = pango_cairo_font_map_get_default ();
+  klass->pango_context =
+      pango_cairo_font_map_create_context (PANGO_CAIRO_FONT_MAP (fontmap));
 
   g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_TEXT,
       g_param_spec_string ("text", "text",
@@ -412,6 +427,15 @@
       g_param_spec_boolean ("wait-text", "Wait Text",
           "Whether to wait for subtitles",
           DEFAULT_PROP_WAIT_TEXT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+  g_object_class_install_property (G_OBJECT_CLASS (klass),
+      PROP_AUTO_ADJUST_SIZE, g_param_spec_boolean ("auto-resize", "auto resize",
+          "Automatically adjust font size to screen-size.",
+          DEFAULT_PROP_AUTO_ADJUST_SIZE, G_PARAM_READWRITE));
+  g_object_class_install_property (G_OBJECT_CLASS (klass),
+      PROP_VERTICAL_RENDER, g_param_spec_boolean ("vertical-render",
+          "vertical render", "Vertical Render.", DEFAULT_PROP_VERTICAL_RENDER,
+          G_PARAM_READWRITE));
 }
 
 static void
@@ -420,8 +444,12 @@
   GstTextOverlay *overlay = GST_TEXT_OVERLAY (object);
 
   g_free (overlay->default_text);
-  g_free (overlay->bitmap.buffer);
 
+  if (overlay->text_image) {
+    g_free (overlay->text_image);
+    overlay->text_image = NULL;
+  }
+ 
   if (overlay->layout) {
     g_object_unref (overlay->layout);
     overlay->layout = NULL;
@@ -449,6 +477,7 @@
 gst_text_overlay_init (GstTextOverlay * overlay, GstTextOverlayClass * klass)
 {
   GstPadTemplate *template;
+  PangoFontDescription *desc;
 
   /* video sink */
   template = gst_static_pad_template_get (&video_sink_template_factory);
@@ -495,9 +524,10 @@
   overlay->line_align = DEFAULT_PROP_LINE_ALIGNMENT;
   overlay->layout =
       pango_layout_new (GST_TEXT_OVERLAY_GET_CLASS (overlay)->pango_context);
-  pango_layout_set_alignment (overlay->layout,
-      (PangoAlignment) overlay->line_align);
-  memset (&overlay->bitmap, 0, sizeof (overlay->bitmap));
+  desc =
+      pango_context_get_font_description (GST_TEXT_OVERLAY_GET_CLASS (overlay)->
+      pango_context);
+  gst_text_overlay_adjust_values_with_fontdesc (overlay, desc);
 
   overlay->halign = DEFAULT_PROP_HALIGNMENT;
   overlay->valign = DEFAULT_PROP_VALIGNMENT;
@@ -512,9 +542,13 @@
   overlay->shading_value = DEFAULT_SHADING_VALUE;
   overlay->silent = DEFAULT_PROP_SILENT;
   overlay->wait_text = DEFAULT_PROP_WAIT_TEXT;
+  overlay->auto_adjust_size = DEFAULT_PROP_AUTO_ADJUST_SIZE;
 
   overlay->default_text = g_strdup (DEFAULT_PROP_TEXT);
   overlay->need_render = TRUE;
+  overlay->text_image = NULL;
+  overlay->use_vertical_render = DEFAULT_PROP_VERTICAL_RENDER;
+  gst_text_overlay_update_render_mode (overlay);
 
   overlay->fps_n = 0;
   overlay->fps_d = 1;
@@ -538,13 +572,48 @@
     GST_DEBUG_OBJECT (overlay, "Set wrap mode NONE");
     pango_layout_set_width (overlay->layout, -1);
   } else {
+    int width;
+
+    if (overlay->auto_adjust_size) {
+      width = DEFAULT_SCALE_BASIS * PANGO_SCALE;
+      if (overlay->use_vertical_render) {
+        width = width * (overlay->height - overlay->ypad * 2) / overlay->width;
+      }
+    } else {
+      width =
+          (overlay->use_vertical_render ? overlay->height : overlay->width) *
+          PANGO_SCALE;
+    }
+
     GST_DEBUG_OBJECT (overlay, "Set layout width %d", overlay->width);
     GST_DEBUG_OBJECT (overlay, "Set wrap mode    %d", overlay->wrap_mode);
-    pango_layout_set_width (overlay->layout, overlay->width * PANGO_SCALE);
+    pango_layout_set_width (overlay->layout, width);
     pango_layout_set_wrap (overlay->layout, (PangoWrapMode) overlay->wrap_mode);
   }
 }
 
+static void
+gst_text_overlay_update_render_mode (GstTextOverlay * overlay)
+{
+#if HAVE_PANGO_VERTICAL_WRITING
+  PangoMatrix matrix = PANGO_MATRIX_INIT;
+  PangoContext *context = pango_layout_get_context (overlay->layout);
+
+  if (overlay->use_vertical_render) {
+    pango_matrix_rotate (&matrix, -90);
+    pango_context_set_base_gravity (context, PANGO_GRAVITY_AUTO);
+    pango_context_set_matrix (context, &matrix);
+    pango_layout_set_alignment (overlay->layout, PANGO_ALIGN_LEFT);
+  } else {
+    pango_context_set_base_gravity (context, PANGO_GRAVITY_SOUTH);
+    pango_context_set_matrix (context, &matrix);
+#endif
+    pango_layout_set_alignment (overlay->layout, overlay->line_align);
+#if HAVE_PANGO_VERTICAL_WRITING
+  }
+#endif
+}
+
 static gboolean
 gst_text_overlay_setcaps_txt (GstPad * pad, GstCaps * caps)
 {
@@ -680,6 +749,7 @@
       if (desc) {
         GST_LOG_OBJECT (overlay, "font description set: %s", fontdesc_str);
         pango_layout_set_font_description (overlay->layout, desc);
+        gst_text_overlay_adjust_values_with_fontdesc (overlay, desc);
         pango_font_description_free (desc);
       } else {
         GST_WARNING_OBJECT (overlay, "font description parse failed: %s",
@@ -698,6 +768,19 @@
     case PROP_WAIT_TEXT:
       overlay->wait_text = g_value_get_boolean (value);
       break;
+    case PROP_AUTO_ADJUST_SIZE:
+    {
+      overlay->auto_adjust_size = g_value_get_boolean (value);
+      overlay->need_render = TRUE;
+    }
+#ifdef HAVE_PANGO_VERTICAL_WRITING
+    case PROP_VERTICAL_RENDER:
+    {
+      overlay->use_vertical_render = g_value_get_boolean (value);
+      gst_text_overlay_update_render_mode (overlay);
+      overlay->need_render = TRUE;
+    }
+#endif
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -751,6 +834,14 @@
     case PROP_WAIT_TEXT:
       g_value_set_boolean (value, overlay->wait_text);
       break;
+    case PROP_AUTO_ADJUST_SIZE:
+      g_value_set_boolean (value, overlay->auto_adjust_size);
+      break;
+#ifdef HAVE_PANGO_VERTICAL_WRITING
+    case PROP_VERTICAL_RENDER:
+      g_value_set_boolean (value, overlay->use_vertical_render);
+      break;
+#endif
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -857,170 +948,297 @@
   return caps;
 }
 
-#define BOX_XPAD         6
-#define BOX_YPAD         6
-
-static inline void
-gst_text_overlay_shade_y (GstTextOverlay * overlay, guchar * dest,
-    gint x0, gint x1, gint y0, gint y1)
+static void
+gst_text_overlay_adjust_values_with_fontdesc (GstTextOverlay * overlay,
+    PangoFontDescription * desc)
 {
-  gint i, j, dest_stride;
-
-  dest_stride = gst_video_format_get_row_stride (GST_VIDEO_FORMAT_I420, 0,
-      overlay->width);
-
-  x0 = CLAMP (x0 - BOX_XPAD, 0, overlay->width);
-  x1 = CLAMP (x1 + BOX_XPAD, 0, overlay->width);
-
-  y0 = CLAMP (y0 - BOX_YPAD, 0, overlay->height);
-  y1 = CLAMP (y1 + BOX_YPAD, 0, overlay->height);
+  gint font_size = pango_font_description_get_size (desc) / PANGO_SCALE;
 
-  for (i = y0; i < y1; ++i) {
-    for (j = x0; j < x1; ++j) {
-      gint y = dest[(i * dest_stride) + j] + overlay->shading_value;
-
-      dest[(i * dest_stride) + j] = CLAMP (y, 0, 255);
-    }
-  }
+  overlay->shadow_offset = (double) (font_size) / 13.0;
+  overlay->outline_offset = (double) (font_size) / 15.0;
+  if (overlay->outline_offset < MINIMUM_OUTLINE_OFFSET)
+    overlay->outline_offset = MINIMUM_OUTLINE_OFFSET;
 }
 
-/* FIXME:
- *  - use proper strides and offset for I420
- *  - don't draw over the edge of the picture (try a longer
- *    text with a huge font size)
- */
-
 static inline void
-gst_text_overlay_blit_yuv420 (GstTextOverlay * overlay, FT_Bitmap * bitmap,
-    guint8 * yuv_pixels, gint x0, gint y0)
+gst_text_overlay_blit_1 (GstTextOverlay * overlay, guchar * dest, gint xpos,
+    gint ypos, guchar * text_image, guint dest_stride)
 {
-  int y;                        /* text bitmap coordinates */
-  int x1;                       /* video buffer coordinates */
-  guint8 *y_p, *bitp, *u_p, *v_p;
-  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;
-  int y_stride, u_stride, v_stride;
-  int u_offset, v_offset;
-  int h, w;
-  guint8 v;
-
-  w = overlay->width;
-  h = overlay->height;
-
-  y_stride = gst_video_format_get_row_stride (GST_VIDEO_FORMAT_I420, 0, w);
-  u_stride = gst_video_format_get_row_stride (GST_VIDEO_FORMAT_I420, 1, w);
-  v_stride = gst_video_format_get_row_stride (GST_VIDEO_FORMAT_I420, 2, w);
-  u_offset =
-      gst_video_format_get_component_offset (GST_VIDEO_FORMAT_I420, 1, w, h);
-  v_offset =
-      gst_video_format_get_component_offset (GST_VIDEO_FORMAT_I420, 2, w, h);
-
-/*
-  if (x0 < 0 && abs (x0) < bitmap_width) {
-    bitmap_x0 = abs (x0);
-    x0 = 0;
-  }
-*/
-
-  if (x0 + bitmap_x0 + bitmap_width > w - 1)    /* 1 pixel border */
-    bitmap_width -= x0 + bitmap_x0 + bitmap_width - w + 1;
-  if (y0 + bitmap_y0 + bitmap_height > h - 1)   /* 1 pixel border */
-    bitmap_height -= y0 + bitmap_y0 + bitmap_height - h + 1;
-
-  x1 = x0 + bitmap_x0;
-
-  /* draw an outline around the text */
-  for (y = bitmap_y0; y < bitmap_y0 + bitmap_height; y++) {
-    int n;
-
-    bitp = bitmap->buffer + (y * bitmap->pitch) + bitmap_x0;
-    y_p = yuv_pixels + ((y + y0) * y_stride) + x1;
-    for (n = bitmap_width; n > 0; --n) {
-      v = *bitp;
-      if (v) {
-        y_p[-1] = CLAMP (y_p[-1] - v, 0, 255);
-        y_p[1] = CLAMP (y_p[1] - v, 0, 255);
-        y_p[-w] = CLAMP (y_p[-w] - v, 0, 255);
-        y_p[w] = CLAMP (y_p[w] - v, 0, 255);
+  gint i, j = 0;
+  gint x, y;
+  guchar r, g, b, a;
+  guchar *pimage;
+  guchar *py;
+  gint width = overlay->image_width;
+  gint height = overlay->image_height;
+
+  if (xpos < 0) {
+    xpos = 0;
+  }
+
+  if (xpos + width > overlay->width) {
+    width = overlay->width - xpos;
+  }
+
+  if (ypos + height > overlay->height) {
+    height = overlay->height - ypos;
+  }
+
+  dest +=  (ypos / 1) * dest_stride;
+
+  for (i = 0; i < height; i++) {
+    pimage = text_image + 4 * (i * overlay->image_width);
+    py = dest + i * dest_stride + xpos;
+    for (j = 0; j < width; j++) {
+      b = *pimage++;
+      g = *pimage++;
+      r = *pimage++;
+      a = *pimage++;
+      if (a == 0) {
+        py++;
+        continue;
       }
-      y_p++;
-      bitp++;
+      y = (int) (((19595 * r) >> 16) + ((38470 * g) >> 16) +
+          ((7471 * b) >> 16));
+      y = CLAMP (y, 0, 255);
+      x = *py;
+      *py++ = (y * a + x * (255 - a)) / 255;
     }
   }
+}
 
-  /* now blit text */
-  x1 = x0 + bitmap_x0;
-  skip_y = 0;
-  for (y = bitmap_y0; y < bitmap_y0 + bitmap_height; y++) {
-    int n;
-
-    bitp = bitmap->buffer + (y * bitmap->pitch) + bitmap_x0;
-
-    y_p = yuv_pixels + 0 + ((y0 + y) * y_stride) + x1;
-    u_p = yuv_pixels + u_offset + (((y0 + y) / 2) * u_stride) + (x1 / 2);
-    v_p = yuv_pixels + v_offset + (((y0 + y) / 2) * v_stride) + (x1 / 2);
-
-    skip_x = 0;
-    for (n = bitmap_width; n > 0; --n) {
-      v = *bitp;
-      if (v) {
-        *y_p = v;
-        if (!skip_y) {
-          *u_p = 0x80;
-          *v_p = 0x80;
-        }
-      }
-      if (!skip_y) {
-        if (!skip_x) {
-          u_p++;
-          v_p++;
-        }
-        skip_x = !skip_x;
+static inline void
+gst_text_overlay_blit_sub2x2cbcr (GstTextOverlay * overlay,
+    guchar * destcb, guchar * destcr, gint xpos, gint ypos, guchar * text_image,
+    guint destcb_stride, guint destcr_stride)
+{
+  gint i, j;
+  gint x, cb, cr;
+  gushort r, g, b, a;
+  guchar *pimage1, *pimage2;
+  guchar *pcb, *pcr;
+  gint width = overlay->image_width - 2;
+  gint height = overlay->image_height - 2;
+
+  if (xpos < 0) {
+    xpos = 0;
+  }
+
+  if (xpos + width > overlay->width) {
+    width = overlay->width - xpos;
+  }
+
+  if (ypos + height > overlay->height) {
+    height = overlay->height - ypos;
+  }
+
+  destcb += (ypos/2) * destcb_stride;
+  destcr += (ypos/2) * destcr_stride;
+
+  for (i = 0; i < height; i += 2) {
+    pimage1 = text_image + 4 * (i * overlay->image_width);
+    pimage2 = pimage1 + 4 * overlay->image_width;
+    pcb = destcb + (i / 2) * destcb_stride + xpos / 2;
+    pcr = destcr + (i / 2) * destcr_stride + xpos / 2;
+    for (j = 0; j < width; j += 2) {
+      b = *pimage1++;
+      g = *pimage1++;
+      r = *pimage1++;
+      a = *pimage1++;
+
+      b += *pimage1++;
+      g += *pimage1++;
+      r += *pimage1++;
+      a += *pimage1++;
+
+      b += *pimage2++;
+      g += *pimage2++;
+      r += *pimage2++;
+      a += *pimage2++;
+
+      /* for rounding */
+      b += *pimage2++ + 2;
+      g += *pimage2++ + 2;
+      r += *pimage2++ + 2;
+      a += *pimage2++ + 2;
+
+      b /= 4;
+      g /= 4;
+      r /= 4;
+      a /= 4;
+
+      if (a == 0) {
+        pcb++;
+        pcr++;
+        continue;
       }
-      y_p++;
-      bitp++;
+      cb = (int) (-((11059 * r) >> 16) - ((21709 * g) >> 16) +
+          ((32768 * b) >> 16) + 128);
+      cb = CLAMP (cb, 0, 255);
+
+      cr = (int) (((32768 * r) >> 16) - ((27439 * g) >> 16) -
+          ((5329 * b) >> 16) + 128);
+      cr = CLAMP (cr, 0, 255);
+
+      x = *pcb;
+      *pcb++ = (cb * a + x * (255 - a)) / 255;
+
+      x = *pcr;
+      *pcr++ = (cr * a + x * (255 - a)) / 255;
     }
-    skip_y = !skip_y;
   }
 }
 
 static void
-gst_text_overlay_resize_bitmap (GstTextOverlay * overlay, gint width,
-    gint height)
+gst_text_overlay_render_pangocairo (GstTextOverlay * overlay,
+    const gchar * string, gint textlen)
 {
-  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;
+  cairo_t *cr;
+  cairo_surface_t *surface;
+  cairo_t *cr_shadow;
+  cairo_surface_t *surface_shadow;
+  PangoRectangle ink_rect, logical_rect;
+  cairo_matrix_t cairo_matrix;
+  int width, height;
+  double scalef = 1.0;
+
+  if (overlay->auto_adjust_size) {
+    /* 640 pixel is default */
+    scalef = (double) (overlay->width) / DEFAULT_SCALE_BASIS;
+  }
+  pango_layout_set_width (overlay->layout, -1);
+  /* set text on pango layout */
+  pango_layout_set_markup (overlay->layout, string, textlen);
+
+  /* get subtitle image size */
+  pango_layout_get_pixel_extents (overlay->layout, &ink_rect, &logical_rect);
+
+  width = (logical_rect.width + overlay->shadow_offset) * scalef;
+
+  if (width + overlay->deltax >
+      (overlay->use_vertical_render ? overlay->height : overlay->width)) {
+    /* 
+     * subtitle image width is larger then overlay width 
+     * so rearrange overlay wrap mode.
+     */
+    gst_text_overlay_update_wrap_mode (overlay);
+    pango_layout_get_pixel_extents (overlay->layout, &ink_rect, &logical_rect);
+    width = overlay->width;
+  }
+
+  height =
+      (logical_rect.height + logical_rect.y + overlay->shadow_offset) * scalef;
+  if (height > overlay->height) {
+    height = overlay->height;
+  }
+#ifdef HAVE_PANGO_VERTICAL_WRITING
+  if (overlay->use_vertical_render) {
+    PangoRectangle rect;
+    PangoContext *context;
+
+    context = pango_layout_get_context (overlay->layout);
+
+    PangoMatrix matrix = PANGO_MATRIX_INIT;
+
+    pango_matrix_rotate (&matrix, -90);
+
+    rect.x = rect.y = 0;
+    rect.width = width;
+    rect.height = height;
+    pango_matrix_transform_pixel_rectangle (&matrix, &rect);
+    matrix.x0 = -rect.x;
+    matrix.y0 = -rect.y;
+
+    pango_context_set_matrix (context, &matrix);
+
+    cairo_matrix.xx = matrix.xx;
+    cairo_matrix.yx = matrix.yx;
+    cairo_matrix.xy = matrix.xy;
+    cairo_matrix.yy = matrix.yy;
+    cairo_matrix.x0 = matrix.x0;
+    cairo_matrix.y0 = matrix.y0;
+    cairo_matrix_scale (&cairo_matrix, scalef, scalef);
+
+    int tmp;
+
+    tmp = height;
+    height = width;
+    width = tmp;
+  } else
+#endif
+  {
+    cairo_matrix_init_scale (&cairo_matrix, scalef, scalef);
   }
-  if (!bitmap->buffer) {
-    /* initialize */
-    bitmap->pixel_mode = ft_pixel_mode_grays;
-    bitmap->num_grays = 256;
-  }
-  overlay->bitmap_buffer_size = size;
-  bitmap->buffer = g_realloc (bitmap->buffer, size);
-  memset (bitmap->buffer, 0, size);
-  bitmap->rows = height;
-  bitmap->width = width;
-  bitmap->pitch = pitch;
-}
 
+  /* clear shadow surface */
+  surface_shadow = cairo_image_surface_create (CAIRO_FORMAT_A8, width, height);
+  cr_shadow = cairo_create (surface_shadow);
+
+  cairo_set_operator (cr_shadow, CAIRO_OPERATOR_CLEAR);
+
+  cairo_paint (cr_shadow);
+  cairo_set_operator (cr_shadow, CAIRO_OPERATOR_OVER);
+
+  cairo_save (cr_shadow);
+  cairo_set_matrix (cr_shadow, &cairo_matrix);
+
+  cairo_save (cr_shadow);
+  /* draw shadow text */
+  cairo_set_source_rgba (cr_shadow, 0.0, 0.0, 0.0, 0.5);
+  cairo_translate (cr_shadow, overlay->shadow_offset, overlay->shadow_offset);
+  pango_cairo_show_layout (cr_shadow, overlay->layout);
+  cairo_restore (cr_shadow);
+
+  /* draw outline text */
+  cairo_save (cr_shadow);
+  cairo_set_source_rgb (cr_shadow, 0.0, 0.0, 0.0);
+  cairo_set_line_width (cr_shadow, overlay->outline_offset);
+  pango_cairo_layout_path (cr_shadow, overlay->layout);
+  cairo_stroke (cr_shadow);
+  cairo_restore (cr_shadow);
+
+  if (overlay->want_shading) {
+    cairo_paint_with_alpha (cr_shadow, overlay->shading_value);
+  }
+  cairo_restore (cr_shadow);
+  cairo_destroy (cr_shadow);
+
+  /* clear image surface */
+  overlay->text_image = g_realloc (overlay->text_image, 4 * width * height);
+
+  surface = cairo_image_surface_create_for_data (overlay->text_image,
+      CAIRO_FORMAT_ARGB32, width, height, width * 4);
+  cr = cairo_create (surface);
+  cairo_set_operator (cr, CAIRO_OPERATOR_CLEAR);
+  cairo_paint (cr);
+  cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
+
+  /* set default color */
+  cairo_set_source_rgb (cr, 1.0, 1.0, 1.0);
+
+  cairo_save (cr);
+  cairo_set_matrix (cr, &cairo_matrix);
+  /* draw text */
+  pango_cairo_show_layout (cr, overlay->layout);
+  cairo_restore (cr);
+
+  /* composite outline, shadow, and text */
+  cairo_set_operator (cr, CAIRO_OPERATOR_DEST_OVER);
+  cairo_set_source_surface (cr, surface_shadow, 0.0, 0.0);
+  cairo_paint (cr);
+
+  cairo_destroy (cr);
+  cairo_surface_destroy (surface_shadow);
+  cairo_surface_destroy (surface);
+  overlay->image_width = width;
+  overlay->image_height = height;
+  overlay->baseline_y = ink_rect.y;
+}
+   
 static void
 gst_text_overlay_render_text (GstTextOverlay * overlay,
     const gchar * text, gint textlen)
 {
-  PangoRectangle ink_rect, logical_rect;
   gchar *string;
 
   if (!overlay->need_render) {
@@ -1044,13 +1262,7 @@
   /* FIXME: should we check for UTF-8 here? */
 
   GST_DEBUG ("Rendering '%s'", string);
-  pango_layout_set_markup (overlay->layout, string, textlen);
-
-  pango_layout_get_pixel_extents (overlay->layout, &ink_rect, &logical_rect);
-  gst_text_overlay_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;
+  gst_text_overlay_render_pangocairo (overlay, string, textlen);
 
   g_free (string);
 
@@ -1061,31 +1273,46 @@
 gst_text_overlay_push_frame (GstTextOverlay * overlay, GstBuffer * video_frame)
 {
   gint xpos, ypos;
+  gint width, height;
+  guchar *y, *u, *v;
+  GstTextOverlayVAlign valign;
+  GstTextOverlayHAlign halign;
+  int y_stride, u_stride, v_stride;
 
-  video_frame = gst_buffer_make_writable (video_frame);
-
-  switch (overlay->halign) {
+  width = overlay->image_width;
+  height = overlay->image_height;
+ 
+  if (overlay->use_vertical_render)
+    halign = GST_TEXT_OVERLAY_HALIGN_RIGHT;
+  else
+    halign = overlay->halign;
+ 
+  switch (halign) {
     case GST_TEXT_OVERLAY_HALIGN_LEFT:
       xpos = overlay->xpad;
       break;
     case GST_TEXT_OVERLAY_HALIGN_CENTER:
-      xpos = (overlay->width - overlay->bitmap.width) / 2;
+      xpos = (overlay->width - width) / 2;
       break;
     case GST_TEXT_OVERLAY_HALIGN_RIGHT:
-      xpos = overlay->width - overlay->bitmap.width - overlay->xpad;
+      xpos = overlay->width - width - overlay->xpad;
       break;
     default:
       xpos = 0;
   }
   xpos += overlay->deltax;
 
-
-  switch (overlay->valign) {
+  if (overlay->use_vertical_render)
+    valign = GST_TEXT_OVERLAY_VALIGN_TOP;
+  else
+    valign = overlay->valign;
+ 
+  switch (valign) {
     case GST_TEXT_OVERLAY_VALIGN_BOTTOM:
-      ypos = overlay->height - overlay->bitmap.rows - overlay->ypad;
+      ypos = overlay->height - height - overlay->ypad;
       break;
     case GST_TEXT_OVERLAY_VALIGN_BASELINE:
-      ypos = overlay->height - (overlay->bitmap.rows + overlay->ypad);
+      ypos = overlay->height - (height + overlay->ypad);
       break;
     case GST_TEXT_OVERLAY_VALIGN_TOP:
       ypos = overlay->ypad;
@@ -1096,17 +1323,32 @@
   }
   ypos += overlay->deltay;
 
-  /* shaded background box */
-  if (overlay->want_shading) {
-    gst_text_overlay_shade_y (overlay,
-        GST_BUFFER_DATA (video_frame), xpos, xpos + overlay->bitmap.width,
-        ypos, ypos + overlay->bitmap.rows);
-  }
+  if (ypos < 0)
+    ypos = 0;
+
 
+  y_stride = gst_video_format_get_row_stride (GST_VIDEO_FORMAT_I420, 0,
+      overlay->width);
+  u_stride = gst_video_format_get_row_stride (GST_VIDEO_FORMAT_I420, 1,
+      overlay->width);
+  v_stride = gst_video_format_get_row_stride (GST_VIDEO_FORMAT_I420, 2,
+      overlay->width);
 
-  if (overlay->bitmap.buffer) {
-    gst_text_overlay_blit_yuv420 (overlay, &overlay->bitmap,
-        GST_BUFFER_DATA (video_frame), xpos, ypos);
+  y = GST_BUFFER_DATA (video_frame);
+  u = y + gst_video_format_get_component_offset (GST_VIDEO_FORMAT_I420, 1,
+      overlay->width, overlay->height);
+  v = y + gst_video_format_get_component_offset (GST_VIDEO_FORMAT_I420, 2,
+      overlay->width, overlay->height);
+
+  /* blit text on video image */
+  if (overlay->text_image) {
+    gst_text_overlay_blit_1 (overlay,
+        y, xpos, ypos,
+        overlay->text_image, y_stride);
+    gst_text_overlay_blit_sub2x2cbcr (overlay,
+        u, v, xpos, ypos,
+        overlay->text_image, u_stride,
+        v_stride);
   }
 
   return gst_pad_push (overlay->srcpad, video_frame);
Index: ext/pango/gsttextoverlay.h
===================================================================
RCS file: /cvs/gstreamer/gst-plugins-base/ext/pango/gsttextoverlay.h,v
retrieving revision 1.16
diff -u -r1.16 gsttextoverlay.h
--- ext/pango/gsttextoverlay.h	24 Mar 2008 12:03:02 -0000	1.16
+++ ext/pango/gsttextoverlay.h	20 Jun 2008 08:55:40 -0000
@@ -2,7 +2,7 @@
 #define __GST_TEXT_OVERLAY_H__
 
 #include <gst/gst.h>
-#include <pango/pangoft2.h>
+#include <pango/pangocairo.h>
 
 G_BEGIN_DECLS
 
@@ -125,15 +125,20 @@
     gboolean                 wait_text;
 
     PangoLayout             *layout;
-    FT_Bitmap                bitmap;
-    gint                     bitmap_buffer_size;
+    gdouble                  shadow_offset;
+    gdouble                  outline_offset;
+    guchar                  *text_image;
+    gint                     image_width;
+    gint                     image_height;
     gint                     baseline_y;
 
+    gboolean                 auto_adjust_size;
     gboolean                 need_render;
 
     gint                     shading_value;  /* for timeoverlay subclass */
 
     gboolean                 have_pango_markup;
+    gboolean                 use_vertical_render;
 };
 
 struct _GstTextOverlayClass {
Index: ext/pango/gsttextrender.c
===================================================================
RCS file: /cvs/gstreamer/gst-plugins-base/ext/pango/gsttextrender.c,v
retrieving revision 1.10
diff -u -r1.10 gsttextrender.c
--- ext/pango/gsttextrender.c	22 Mar 2008 15:00:43 -0000	1.10
+++ ext/pango/gsttextrender.c	20 Jun 2008 08:55:40 -0000
@@ -1,6 +1,7 @@
 /* GStreamer
  * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
  * Copyright (C) <2003> David Schleef <ds@schleef.org>
+ * Copyright (C) <2008> Young-Ho Cha <ganadist@gmail.com>
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Library General Public
@@ -50,10 +51,13 @@
 #include <gst/video/video.h>
 
 #include "gsttextrender.h"
+#include <string.h>
 
 GST_DEBUG_CATEGORY_EXTERN (pango_debug);
 #define GST_CAT_DEFAULT pango_debug
 
+#define MINIMUM_OUTLINE_OFFSET 1.0
+
 static const GstElementDetails text_render_details =
 GST_ELEMENT_DETAILS ("Text renderer",
     "Filter/Editor/Video",
@@ -82,6 +86,9 @@
     GST_STATIC_CAPS ("text/x-pango-markup; text/plain")
     );
 
+static void gst_text_render_adjust_values_with_fontdesc (GstTextRender *
+    render, PangoFontDescription * desc);
+
 GST_BOILERPLATE (GstTextRender, gst_text_render, GstElement, GST_TYPE_ELEMENT)
 
      static void gst_text_render_finalize (GObject * object);
@@ -105,6 +112,7 @@
 {
   GObjectClass *gobject_class;
   GstElementClass *gstelement_class;
+  PangoFontMap *fontmap;
 
   gobject_class = (GObjectClass *) klass;
   gstelement_class = (GstElementClass *) klass;
@@ -114,7 +122,9 @@
   gobject_class->finalize = gst_text_render_finalize;
   gobject_class->set_property = gst_text_render_set_property;
 
-  klass->pango_context = pango_ft2_get_context (72, 72);
+  fontmap = pango_cairo_font_map_get_default ();
+  klass->pango_context =
+      pango_cairo_font_map_create_context (PANGO_CAIRO_FONT_MAP (fontmap));
   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 "
@@ -124,47 +134,85 @@
           " for syntax.", "", G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS));
 }
 
-
 static void
-resize_bitmap (GstTextRender * render, gint width, gint height)
+gst_text_render_adjust_values_with_fontdesc (GstTextRender * render,
+    PangoFontDescription * desc)
 {
-  FT_Bitmap *bitmap = &render->bitmap;
-  gint pitch = (width | 3) + 1;
-  gint size = pitch * height;
-
-  /* no need to keep reallocating; just keep the maximum size so far */
-  if (size <= render->bitmap_buffer_size) {
-    bitmap->rows = height;
-    bitmap->width = width;
-    bitmap->pitch = pitch;
-    memset (bitmap->buffer, 0, render->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);
-  render->bitmap_buffer_size = size;
+  gint font_size = pango_font_description_get_size (desc) / PANGO_SCALE;
+
+  render->shadow_offset = (double) (font_size) / 13.0;
+  render->outline_offset = (double) (font_size) / 15.0;
+  if (render->outline_offset < MINIMUM_OUTLINE_OFFSET)
+    render->outline_offset = MINIMUM_OUTLINE_OFFSET;
 }
 
 static void
-gst_text_render_render_text (GstTextRender * render)
+gst_text_render_render_pangocairo (GstTextRender * render)
 {
+  cairo_t *cr;
+  cairo_surface_t *surface;
+  cairo_t *cr_shadow;
+  cairo_surface_t *surface_shadow;
   PangoRectangle ink_rect, logical_rect;
+  gint width, height;
 
   pango_layout_get_pixel_extents (render->layout, &ink_rect, &logical_rect);
-  resize_bitmap (render, ink_rect.width, ink_rect.height + ink_rect.y);
-  pango_ft2_render_layout (&render->bitmap, render->layout, -ink_rect.x, 0);
-  render->baseline_y = ink_rect.y;
+
+  width = logical_rect.width + render->shadow_offset;
+  height = logical_rect.height + logical_rect.y + render->shadow_offset;
+
+  surface_shadow = cairo_image_surface_create (CAIRO_FORMAT_A8, width, height);
+  cr_shadow = cairo_create (surface_shadow);
+
+  /* clear shadow surface */
+  cairo_set_source_rgba (cr_shadow, 0.0, 0.0, 0.0, 0.0);
+  cairo_set_operator (cr_shadow, CAIRO_OPERATOR_CLEAR);
+  cairo_paint (cr_shadow);
+  cairo_set_operator (cr_shadow, CAIRO_OPERATOR_OVER);
+
+  cairo_set_source_rgb (cr_shadow, 0.0, 0.0, 0.0);
+  pango_cairo_update_layout (cr_shadow, render->layout);
+
+  /* draw shadow text */
+  cairo_save (cr_shadow);
+  cairo_set_source_rgba (cr_shadow, 0.0, 0.0, 0.0, 0.5);
+  cairo_translate (cr_shadow, render->shadow_offset, render->shadow_offset);
+  pango_cairo_show_layout (cr_shadow, render->layout);
+  cairo_restore (cr_shadow);
+
+  /* draw outline text */
+  cairo_save (cr_shadow);
+  cairo_set_line_width (cr_shadow, render->outline_offset);
+  pango_cairo_layout_path (cr_shadow, render->layout);
+  cairo_stroke (cr_shadow);
+  cairo_restore (cr_shadow);
+
+  cairo_destroy (cr_shadow);
+
+  render->text_image = g_realloc (render->text_image, 4 * width * height);
+  memset (render->text_image, 0, 4 * width * height);
+
+  surface = cairo_image_surface_create_for_data (render->text_image,
+      CAIRO_FORMAT_ARGB32, width, height, width * 4);
+  cr = cairo_create (surface);
+
+  /* set default color */
+  cairo_set_source_rgb (cr, 1.0, 1.0, 1.0);
+
+  /* draw text */
+  pango_cairo_update_layout (cr, render->layout);
+  pango_cairo_show_layout (cr, render->layout);
+
+  /* composite shadow with offset */
+  cairo_set_operator (cr, CAIRO_OPERATOR_DEST_OVER);
+  cairo_set_source_surface (cr, surface_shadow, 0.0, 0.0);
+  cairo_paint (cr);
+
+  cairo_destroy (cr);
+  cairo_surface_destroy (surface_shadow);
+  cairo_surface_destroy (surface);
+  render->image_width = width;
+  render->image_height = height;
 }
 
 static gboolean
@@ -181,7 +229,7 @@
 
   GST_DEBUG ("Got caps %" GST_PTR_FORMAT, caps);
 
-  if (width >= render->bitmap.width && height >= render->bitmap.rows) {
+  if (width >= render->image_width && height >= render->image_height) {
     render->width = width;
     render->height = height;
     ret = TRUE;
@@ -198,44 +246,48 @@
   GstStructure *s = gst_caps_get_structure (caps, 0);
 
   GST_DEBUG ("Fixating caps %" GST_PTR_FORMAT, caps);
-  gst_structure_fixate_field_nearest_int (s, "width", render->bitmap.width);
-  gst_structure_fixate_field_nearest_int (s, "height", render->bitmap.rows);
+  gst_structure_fixate_field_nearest_int (s, "width", render->image_width);
+  gst_structure_fixate_field_nearest_int (s, "height", render->image_height);
   GST_DEBUG ("Fixated to    %" GST_PTR_FORMAT, caps);
 
   gst_object_unref (render);
 }
 
 static void
-gst_text_renderer_bitmap_to_ayuv (GstTextRender * render, FT_Bitmap * bitmap,
-    guchar * pixbuf)
+gst_text_renderer_argb_to_ayuv (GstTextRender * render, guchar * pixbuf)
 {
   int y;                        /* text bitmap coordinates */
-  int rowinc, bit_rowinc;
+  int row_inc;
   guchar *p, *bitp;
-  guchar v;
+  guchar a, r, g, b;
+  int width, height;
 
-  rowinc = render->width - bitmap->width;
-  bit_rowinc = bitmap->pitch - bitmap->width;
-
-  bitp = bitmap->buffer;
+  width = render->image_width;
+  height = render->image_height;
+  bitp = render->text_image;
   p = pixbuf;
 
-  for (y = 0; y < bitmap->rows; y++) {
+  row_inc = render->width - width;
+  if (row_inc < 0)
+    row_inc = 0;
+
+  for (y = 0; y < height; y++) {
     int n;
 
-    for (n = bitmap->width; n > 0; --n) {
-      v = *bitp;
-      if (v) {
-        p[0] = v;
-        p[1] = 255;
-        p[2] = 0x80;
-        p[3] = 0x80;
-      }
-      p += 4;
-      bitp++;
+    for (n = 0; n < width; n++) {
+      b = *bitp++;
+      g = *bitp++;
+      r = *bitp++;
+      a = *bitp++;
+
+      *p++ = a;
+      *p++ = CLAMP ((int) (((19595 * r) >> 16) + ((38470 * g) >> 16) +
+              ((7471 * b) >> 16)), 0, 255);
+      *p++ = CLAMP ((int) (-((11059 * r) >> 16) - ((21709 * g) >> 16) +
+              ((32768 * b) >> 16) + 128), 0, 255);
+      *p++ = CLAMP ((int) (((32768 * r) >> 16) - ((27439 * g) >> 16) -
+              ((5329 * b) >> 16) + 128), 0, 255);
     }
-    p += rowinc * 4;
-    bitp += bit_rowinc;
   }
 }
 
@@ -249,7 +301,6 @@
   GstCaps *caps = NULL;
   guint8 *data = GST_BUFFER_DATA (inbuf);
   guint size = GST_BUFFER_SIZE (inbuf);
-  gint n;
 
   render = GST_TEXT_RENDER (gst_pad_get_parent (pad));
 
@@ -260,14 +311,19 @@
     size--;
   }
 
+  if (size == 0) {
+    size = 1;
+    data = (guint8 *) " ";
+  }
+
   /* render text */
   GST_DEBUG ("rendering '%*s'", size, data);
   pango_layout_set_markup (render->layout, (gchar *) data, size);
-  gst_text_render_render_text (render);
+  gst_text_render_render_pangocairo (render);
 
   caps = gst_caps_new_simple ("video/x-raw-yuv", "format", GST_TYPE_FOURCC,
       GST_MAKE_FOURCC ('A', 'Y', 'U', 'V'), "width", G_TYPE_INT,
-      render->bitmap.width, "height", G_TYPE_INT, render->bitmap.rows,
+      render->image_width, "height", G_TYPE_INT, render->image_height,
       "framerate", GST_TYPE_FRACTION, 1, 1, NULL);
 
   if (!gst_pad_set_caps (render->srcpad, caps)) {
@@ -289,14 +345,8 @@
   gst_buffer_copy_metadata (outbuf, inbuf, GST_BUFFER_COPY_TIMESTAMPS);
   data = GST_BUFFER_DATA (outbuf);
 
-  for (n = 0; n < render->width * render->height; n++) {
-    data[n * 4] = 0;
-    data[n * 4 + 1] = 0;
-    data[n * 4 + 2] = data[n * 4 + 3] = 128;
-  }
-
-  if (render->bitmap.buffer) {
-    gst_text_renderer_bitmap_to_ayuv (render, &render->bitmap, data);
+  if (render->text_image) {
+    gst_text_renderer_argb_to_ayuv (render, data);
   }
 
   ret = gst_pad_push (render->srcpad, outbuf);
@@ -314,7 +364,10 @@
 {
   GstTextRender *render = GST_TEXT_RENDER (object);
 
-  g_free (render->bitmap.buffer);
+  if (render->text_image) {
+    g_free (render->text_image);
+    render->text_image = NULL;
+  }
 
   if (render->layout)
     g_object_unref (render->layout);
@@ -326,6 +379,7 @@
 gst_text_render_init (GstTextRender * render, GstTextRenderClass * klass)
 {
   GstPadTemplate *template;
+  PangoFontDescription *desc;
 
   /* sink */
   template = gst_static_pad_template_get (&sink_template_factory);
@@ -347,7 +401,13 @@
 
   render->layout =
       pango_layout_new (GST_TEXT_RENDER_GET_CLASS (render)->pango_context);
-  memset (&render->bitmap, 0, sizeof (render->bitmap));
+  pango_layout_set_width (render->layout, -1);
+  pango_layout_set_alignment (render->layout, PANGO_ALIGN_CENTER);
+  desc =
+      pango_context_get_font_description (GST_TEXT_RENDER_GET_CLASS (render)->
+      pango_context);
+  gst_text_render_adjust_values_with_fontdesc (render, desc);
+
 }
 
 static void
@@ -366,8 +426,9 @@
         GST_LOG ("font description set: %s", g_value_get_string (value));
         GST_OBJECT_LOCK (render);
         pango_layout_set_font_description (render->layout, desc);
+        gst_text_render_adjust_values_with_fontdesc (render, desc);
         pango_font_description_free (desc);
-        gst_text_render_render_text (render);
+        gst_text_render_render_pangocairo (render);
         GST_OBJECT_UNLOCK (render);
       } else {
         GST_WARNING ("font description parse failed: %s",
Index: ext/pango/gsttextrender.h
===================================================================
RCS file: /cvs/gstreamer/gst-plugins-base/ext/pango/gsttextrender.h,v
retrieving revision 1.4
diff -u -r1.4 gsttextrender.h
--- ext/pango/gsttextrender.h	8 Apr 2006 18:09:17 -0000	1.4
+++ ext/pango/gsttextrender.h	20 Jun 2008 08:55:40 -0000
@@ -2,7 +2,7 @@
 #define __GST_TEXT_RENDER_H__
 
 #include <gst/gst.h>
-#include <pango/pangoft2.h>
+#include <pango/pangocairo.h>
 
 G_BEGIN_DECLS
 
@@ -33,8 +33,11 @@
     gint                  width;
     gint                  height;
     PangoLayout          *layout;
-    FT_Bitmap             bitmap;
-    gint                  bitmap_buffer_size;
+    gdouble               shadow_offset;
+    gdouble               outline_offset;
+    guchar               *text_image;
+    gint                  image_width;
+    gint                  image_height;
     gint                  baseline_y;
 };
 
