pango: Use pango-cairo instead of pango-ft2
authorYoung-Ho Cha <ganadist@chollian.net>
Tue, 4 Aug 2009 09:35:10 +0000 (11:35 +0200)
committerSebastian Dröge <sebastian.droege@collabora.co.uk>
Thu, 6 Aug 2009 04:43:36 +0000 (06:43 +0200)
pango-cairo will always use the native font rendering backend
of the platform and provides better results.

Fixes bug #340887.

ext/pango/gsttextoverlay.c
ext/pango/gsttextoverlay.h
ext/pango/gsttextrender.c
ext/pango/gsttextrender.h

index 168e911..5eeecd5 100644 (file)
@@ -4,6 +4,7 @@
  * Copyright (C) <2006> Julien Moutte <julien@moutte.net>
  * Copyright (C) <2006> Zeeshan Ali <zeeshan.ali@nokia.com>
  * Copyright (C) <2006-2008> Tim-Philipp Müller <tim centricular net>
+ * Copyright (C) <2009> 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
@@ -80,6 +81,7 @@
 #include "gsttimeoverlay.h"
 #include "gstclockoverlay.h"
 #include "gsttextrender.h"
+#include <string.h>
 
 /* FIXME:
  *  - use proper strides and offset for I420
@@ -113,10 +115,38 @@ GST_ELEMENT_DETAILS ("Text overlay",
 #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 MINIMUM_OUTLINE_OFFSET 1.0
+#define DEFAULT_SCALE_BASIS    640
+
+#define COMP_Y(ret, r, g, b) \
+{ \
+   ret = (int) (((19595 * r) >> 16) + ((38470 * g) >> 16) + ((7471 * b) >> 16)); \
+   ret = CLAMP (ret, 0, 255); \
+}
+
+#define COMP_U(ret, r, g, b) \
+{ \
+   ret = (int) (-((11059 * r) >> 16) - ((21709 * g) >> 16) + ((32768 * b) >> 16) + 128); \
+   ret = CLAMP (ret, 0, 255); \
+}
+
+#define COMP_V(ret, r, g, b) \
+{ \
+   ret = (int) (((32768 * r) >> 16) - ((27439 * g) >> 16) - ((5329 * b) >> 16) + 128); \
+   ret = CLAMP (ret, 0, 255); \
+}
+
+#define BLEND(ret, alpha, v0, v1) \
+{ \
+       ret = (v0 * alpha + v1 * (255 - alpha)) / 255; \
+}
+
 enum
 {
   PROP_0,
@@ -135,6 +165,8 @@ enum
   PROP_SILENT,
   PROP_LINE_ALIGNMENT,
   PROP_WAIT_TEXT,
+  PROP_AUTO_ADJUST_SIZE,
+  PROP_VERTICAL_RENDER,
   PROP_LAST
 };
 
@@ -265,12 +297,15 @@ static GstPadLinkReturn gst_text_overlay_text_pad_link (GstPad * pad,
     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)
 
@@ -304,6 +339,7 @@ gst_text_overlay_class_init (GstTextOverlayClass * klass)
 {
   GObjectClass *gobject_class;
   GstElementClass *gstelement_class;
+  PangoFontMap *fontmap;
 
   gobject_class = (GObjectClass *) klass;
   gstelement_class = (GstElementClass *) klass;
@@ -316,7 +352,9 @@ gst_text_overlay_class_init (GstTextOverlayClass * klass)
       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",
@@ -408,6 +446,16 @@ gst_text_overlay_class_init (GstTextOverlayClass * klass)
       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
@@ -416,7 +464,11 @@ gst_text_overlay_finalize (GObject * object)
   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);
@@ -445,6 +497,7 @@ static void
 gst_text_overlay_init (GstTextOverlay * overlay, GstTextOverlayClass * klass)
 {
   GstPadTemplate *template;
+  PangoFontDescription *desc;
 
   /* video sink */
   template = gst_static_pad_template_get (&video_sink_template_factory);
@@ -491,9 +544,10 @@ gst_text_overlay_init (GstTextOverlay * overlay, GstTextOverlayClass * klass)
   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;
@@ -508,9 +562,13 @@ gst_text_overlay_init (GstTextOverlay * overlay, GstTextOverlayClass * klass)
   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;
@@ -534,13 +592,48 @@ gst_text_overlay_update_wrap_mode (GstTextOverlay * overlay)
     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)
 {
@@ -677,6 +770,7 @@ gst_text_overlay_set_property (GObject * object, guint prop_id,
       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",
@@ -695,6 +789,19 @@ gst_text_overlay_set_property (GObject * object, guint prop_id,
     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;
@@ -748,6 +855,16 @@ gst_text_overlay_get_property (GObject * object, guint prop_id,
     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;
+    case PROP_VERTICAL_RENDER:
+#ifdef HAVE_PANGO_VERTICAL_WRITING
+      g_value_set_boolean (value, overlay->use_vertical_render);
+#else
+      g_value_set_boolean (value, FALSE);
+#endif
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -854,6 +971,285 @@ gst_text_overlay_getcaps (GstPad * pad)
   return caps;
 }
 
+static void
+gst_text_overlay_adjust_values_with_fontdesc (GstTextOverlay * overlay,
+    PangoFontDescription * desc)
+{
+  gint font_size = pango_font_description_get_size (desc) / PANGO_SCALE;
+  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;
+}
+
+static inline void
+gst_text_overlay_blit_1 (GstTextOverlay * overlay, guchar * dest, gint xpos,
+    gint ypos, guchar * text_image, guint dest_stride)
+{
+  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;
+      }
+      COMP_Y (y, r, g, b);
+      x = *py;
+      BLEND (*py++, a, y, 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;
+      }
+      COMP_U (cb, r, g, b);
+      COMP_V (cr, r, g, b)
+
+          x = *pcb;
+      BLEND (*pcb++, a, cb, x);
+      x = *pcr;
+      BLEND (*pcr++, a, cr, x);
+    }
+  }
+}
+
+static void
+gst_text_overlay_render_pangocairo (GstTextOverlay * overlay,
+    const gchar * string, gint textlen)
+{
+  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;
+    PangoMatrix matrix = PANGO_MATRIX_INIT;
+    int tmp;
+
+    context = pango_layout_get_context (overlay->layout);
+
+    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);
+
+    tmp = height;
+    height = width;
+    width = tmp;
+  } else
+#endif
+  {
+    cairo_matrix_init_scale (&cairo_matrix, scalef, scalef);
+  }
+  /* 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 */
+  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;
+}
+
+
+
 #define BOX_XPAD         6
 #define BOX_YPAD         6
 
@@ -915,21 +1311,12 @@ gst_text_overlay_shade_UYVY_y (GstTextOverlay * overlay, guchar * dest,
  */
 
 static inline void
-gst_text_overlay_blit_I420 (GstTextOverlay * overlay, FT_Bitmap * bitmap,
-    guint8 * yuv_pixels, gint x0, gint y0)
+gst_text_overlay_blit_I420 (GstTextOverlay * overlay,
+    guint8 * yuv_pixels, gint xpos, gint ypos)
 {
-  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;
@@ -942,177 +1329,90 @@ gst_text_overlay_blit_I420 (GstTextOverlay * overlay, FT_Bitmap * bitmap,
   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);
-      }
-      y_p++;
-      bitp++;
-    }
-  }
-
-  /* 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;
-      }
-      y_p++;
-      bitp++;
-    }
-    skip_y = !skip_y;
-  }
+  gst_text_overlay_blit_1 (overlay, yuv_pixels, xpos, ypos, overlay->text_image,
+      y_stride);
+  gst_text_overlay_blit_sub2x2cbcr (overlay, yuv_pixels + u_offset,
+      yuv_pixels + v_offset, xpos, ypos, overlay->text_image, u_stride,
+      v_stride);
 }
 
 static inline void
-gst_text_overlay_blit_UYVY (GstTextOverlay * overlay, FT_Bitmap * bitmap,
-    guint8 * yuv_pixels, gint x0, gint y0)
+gst_text_overlay_blit_UYVY (GstTextOverlay * overlay,
+    guint8 * yuv_pixels, gint xpos, gint ypos)
 {
-  int y;                        /* text bitmap coordinates */
-  int x1, y1;                   /* video buffer coordinates */
-  guint8 *p, *bitp;
-  int video_width, video_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;
-
-  video_width =
-      gst_video_format_get_row_stride (GST_VIDEO_FORMAT_UYVY, 0,
-      overlay->width);
-  video_height = overlay->height;
-
-  /*g_debug ("bliting bitmap(%d, %d) on yuv (%d,%d) at %d,%d",
-     bitmap_width, bitmap_height,
-     video_width, video_height,
-     x0, y0); */
-
-  if (x0 + bitmap_x0 + bitmap_width > overlay->width - 1)       /* 1 pixel border */
-    bitmap_width -= x0 + bitmap_x0 + bitmap_width - overlay->width + 1;
-  if (y0 + bitmap_y0 + bitmap_height > video_height - 1)        /* 1 pixel border */
-    bitmap_height -= y0 + bitmap_y0 + bitmap_height - video_height + 1;
-
-  x1 = x0 + bitmap_x0;
-  y1 = y0 + bitmap_y0;
-
-  /* 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;
-    p = yuv_pixels + (y0 + y) * video_width + (x1 * 2) + 1;
-    for (n = bitmap_width; n > 0; --n) {
-      if (*bitp) {
-        *(p + 2) = CLAMP (*(p + 2) - *bitp, 0, 255);
-        *(p - 2) = CLAMP (*(p - 2) - *bitp, 0, 255);
-        *(p - video_width) = CLAMP (*(p - video_width) - *bitp, 0, 255);
-        *(p + video_width) = CLAMP (*(p + video_width) - *bitp, 0, 255);
-      }
-      p += 2;
-      bitp++;
-    }
+  int a0, r0, g0, b0;
+  int a1, r1, g1, b1;
+  int y0, y1, u, v;
+  int i, j;
+  int h, w;
+  guchar *pimage, *dest;
+
+  w = overlay->image_width - 2;
+  h = overlay->image_height - 2;
+
+  if (xpos < 0) {
+    xpos = 0;
   }
 
-  /* now blit text */
-  for (y = bitmap_y0; y < bitmap_y0 + bitmap_height; y++) {
-    int n;
+  if (xpos + w > overlay->width) {
+    w = overlay->width - xpos;
+  }
 
-    bitp = bitmap->buffer + (y * bitmap->pitch) + bitmap_x0;
-    p = yuv_pixels + (y0 + y) * video_width + (x1 * 2) + 1;
+  if (ypos + h > overlay->height) {
+    h = overlay->height - ypos;
+  }
 
-    for (n = bitmap_width; n > 0; --n) {
-      if (*bitp) {
-        *p = *bitp;
-        *(p - 1) = 0x80;
+  for (i = 0; i < h; i++) {
+    pimage = overlay->text_image + i * overlay->image_width * 4;
+    dest = yuv_pixels + (i + ypos) * overlay->width * 2 + xpos * 2;
+    for (j = 0; j < w; j += 2) {
+      b0 = *pimage++;
+      g0 = *pimage++;
+      r0 = *pimage++;
+      a0 = *pimage++;
+
+      b1 = *pimage++;
+      g1 = *pimage++;
+      r1 = *pimage++;
+      a1 = *pimage++;
+
+      a0 += a1 + 2;
+      a0 /= 2;
+      if (a0 == 0) {
+        dest += 4;
+        continue;
       }
-      p += 2;
-      bitp++;
-    }
-  }
 
-}
+      COMP_Y (y0, r0, g0, b0);
+      COMP_Y (y1, r1, g1, b1);
 
-static void
-gst_text_overlay_resize_bitmap (GstTextOverlay * overlay, gint width,
-    gint 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;
+      b0 += b1 + 2;
+      g0 += g1 + 2;
+      r0 += r1 + 2;
+
+      b0 /= 2;
+      g0 /= 2;
+      r0 /= 2;
+
+      COMP_U (u, r0, g0, b0);
+      COMP_V (v, r0, g0, b0);
+
+      BLEND (*dest, a0, u, *dest);
+      dest++;
+      BLEND (*dest, a0, y0, *dest);
+      dest++;
+      BLEND (*dest, a0, v, *dest);
+      dest++;
+      BLEND (*dest, a0, y1, *dest);
+      dest++;
+    }
   }
-  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;
 }
 
 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) {
@@ -1136,13 +1436,7 @@ gst_text_overlay_render_text (GstTextOverlay * overlay,
   /* 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);
 
@@ -1153,31 +1447,45 @@ static GstFlowReturn
 gst_text_overlay_push_frame (GstTextOverlay * overlay, GstBuffer * video_frame)
 {
   gint xpos, ypos;
+  gint width, height;
+  GstTextOverlayVAlign valign;
+  GstTextOverlayHAlign halign;
+
+  width = overlay->image_width;
+  height = overlay->image_height;
 
-  video_frame = gst_buffer_make_writable (video_frame);
 
-  switch (overlay->halign) {
+  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;
 
+  if (overlay->use_vertical_render)
+    valign = GST_TEXT_OVERLAY_VALIGN_TOP;
+  else
+    valign = overlay->valign;
 
-  switch (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;
@@ -1188,40 +1496,23 @@ gst_text_overlay_push_frame (GstTextOverlay * overlay, GstBuffer * video_frame)
   }
   ypos += overlay->deltay;
 
-  /* shaded background box */
-  if (overlay->want_shading) {
-    switch (overlay->format) {
-      case GST_MAKE_FOURCC ('I', '4', '2', '0'):
-        gst_text_overlay_shade_I420_y (overlay,
-            GST_BUFFER_DATA (video_frame), xpos, xpos + overlay->bitmap.width,
-            ypos, ypos + overlay->bitmap.rows);
-        break;
-      case GST_MAKE_FOURCC ('U', 'Y', 'V', 'Y'):
-        gst_text_overlay_shade_UYVY_y (overlay,
-            GST_BUFFER_DATA (video_frame), xpos, xpos + overlay->bitmap.width,
-            ypos, ypos + overlay->bitmap.rows);
-        break;
-      default:
-        g_assert_not_reached ();
-    }
-  }
-
+  if (ypos < 0)
+    ypos = 0;
 
-  if (overlay->bitmap.buffer) {
+  if (overlay->text_image) {
     switch (overlay->format) {
       case GST_MAKE_FOURCC ('I', '4', '2', '0'):
-        gst_text_overlay_blit_I420 (overlay, &overlay->bitmap,
+        gst_text_overlay_blit_I420 (overlay,
             GST_BUFFER_DATA (video_frame), xpos, ypos);
         break;
       case GST_MAKE_FOURCC ('U', 'Y', 'V', 'Y'):
-        gst_text_overlay_blit_UYVY (overlay, &overlay->bitmap,
+        gst_text_overlay_blit_UYVY (overlay,
             GST_BUFFER_DATA (video_frame), xpos, ypos);
         break;
       default:
         g_assert_not_reached ();
     }
   }
-
   return gst_pad_push (overlay->srcpad, video_frame);
 }
 
index a644ef8..94ef1f8 100644 (file)
@@ -2,7 +2,7 @@
 #define __GST_TEXT_OVERLAY_H__
 
 #include <gst/gst.h>
-#include <pango/pangoft2.h>
+#include <pango/pangocairo.h>
 
 G_BEGIN_DECLS
 
@@ -126,15 +126,20 @@ struct _GstTextOverlay {
     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 175e520..fd8cc72 100644 (file)
@@ -1,6 +1,7 @@
 /* GStreamer
  * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
  * Copyright (C) <2003> David Schleef <ds@schleef.org>
+ * Copyright (C) <2009> 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
 #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",
@@ -150,6 +154,9 @@ gst_text_render_line_align_get_type (void)
   return text_render_line_align_type;
 }
 
+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);
@@ -176,6 +183,7 @@ gst_text_render_class_init (GstTextRenderClass * klass)
 {
   GObjectClass *gobject_class;
   GstElementClass *gstelement_class;
+  PangoFontMap *fontmap;
 
   gobject_class = (GObjectClass *) klass;
   gstelement_class = (GstElementClass *) klass;
@@ -186,7 +194,9 @@ gst_text_render_class_init (GstTextRenderClass * klass)
   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);
+  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 "
@@ -217,47 +227,85 @@ gst_text_render_class_init (GstTextRenderClass * klass)
           G_PARAM_READWRITE | 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 void
@@ -306,7 +354,7 @@ gst_text_render_setcaps (GstPad * pad, GstCaps * caps)
 
   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;
@@ -325,92 +373,62 @@ gst_text_render_fixate_caps (GstPad * pad, GstCaps * caps)
   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->width);
-  gst_structure_fixate_field_nearest_int (s, "height", render->height);
+  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, gint x0, gint x1, gint y0, gint y1)
+gst_text_renderer_image_to_ayuv (GstTextRender * render, guchar * pixbuf,
+    int xpos, int ypos, int stride)
 {
   int y;                        /* text bitmap coordinates */
-  int rowinc, bit_rowinc;
   guchar *p, *bitp;
-  guchar v;
-
-  x0 = CLAMP (x0, 0, render->width);
-  x1 = CLAMP (x1, 0, render->width);
-
-  y0 = CLAMP (y0, 0, render->height);
-  y1 = CLAMP (y1, 0, render->height);
-
-
-  rowinc = render->width - bitmap->width;
-  bit_rowinc = bitmap->pitch - bitmap->width;
+  guchar a, r, g, b;
+  int width, height;
 
-  bitp = bitmap->buffer;
-  p = pixbuf + ((x0 + (render->width * y0)) * 4);
+  width = render->image_width;
+  height = render->image_height;
+  bitp = render->text_image;
 
-  for (y = y0; y < y1; y++) {
+  for (y = 0; y < height; y++) {
     int n;
-
-    for (n = x0; n < x1; n++) {
-      v = *bitp;
-      if (v) {
-        p[0] = v;
-        p[1] = 255;
-        p[2] = 0x80;
-        p[3] = 0x80;
-      }
-      p += 4;
-      bitp++;
+    p = pixbuf + ypos * stride + xpos;
+    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;
   }
 }
 
 static void
-gst_text_renderer_bitmap_to_argb (GstTextRender * render, FT_Bitmap * bitmap,
-    guchar * pixbuf, gint x0, gint x1, gint y0, gint y1)
+gst_text_renderer_image_to_argb (GstTextRender * render, guchar * pixbuf,
+    int xpos, int ypos, int stride)
 {
-  int y;                        /* text bitmap coordinates */
-  int rowinc, bit_rowinc;
+  int i;
   guchar *p, *bitp;
-  guchar v;
-
-  x0 = CLAMP (x0, 0, render->width);
-  x1 = CLAMP (x1, 0, render->width);
-
-  y0 = CLAMP (y0, 0, render->height);
-  y1 = CLAMP (y1, 0, render->height);
-
+  int width, height;
 
-  rowinc = render->width - bitmap->width;
-  bit_rowinc = bitmap->pitch - bitmap->width;
+  width = render->image_width;
+  height = render->image_height;
+  bitp = render->text_image;
 
-  bitp = bitmap->buffer;
-  p = pixbuf + ((x0 + (render->width * y0)) * 4);
-
-  for (y = y0; y < y1; y++) {
-    int n;
-
-    for (n = x0; n < x1; n++) {
-      v = *bitp;
-      if (v) {
-        p[0] = v;
-        p[1] = 255;
-        p[2] = 255;
-        p[3] = 255;
-      }
-      p += 4;
-      bitp++;
-    }
-    p += rowinc * 4;
-    bitp += bit_rowinc;
+  for (i = 0; i < height; i++) {
+    p = pixbuf + ypos * stride + xpos;
+    memcpy (p, bitp, width * 4);
+    bitp += width * 4;
   }
 }
 
@@ -438,7 +456,7 @@ gst_text_render_chain (GstPad * pad, GstBuffer * inbuf)
   /* 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);
 
   gst_text_render_check_argb (render);
 
@@ -487,10 +505,10 @@ gst_text_render_chain (GstPad * pad, GstBuffer * inbuf)
       xpos = render->xpad;
       break;
     case GST_TEXT_RENDER_HALIGN_CENTER:
-      xpos = (render->width - render->bitmap.width) / 2;
+      xpos = (render->width - render->image_width) / 2;
       break;
     case GST_TEXT_RENDER_HALIGN_RIGHT:
-      xpos = render->width - render->bitmap.width - render->xpad;
+      xpos = render->width - render->image_width - render->xpad;
       break;
     default:
       xpos = 0;
@@ -498,10 +516,10 @@ gst_text_render_chain (GstPad * pad, GstBuffer * inbuf)
 
   switch (render->valign) {
     case GST_TEXT_RENDER_VALIGN_BOTTOM:
-      ypos = render->height - render->bitmap.rows - render->ypad;
+      ypos = render->height - render->image_height - render->ypad;
       break;
     case GST_TEXT_RENDER_VALIGN_BASELINE:
-      ypos = render->height - (render->bitmap.rows + render->ypad);
+      ypos = render->height - (render->image_height + render->ypad);
       break;
     case GST_TEXT_RENDER_VALIGN_TOP:
       ypos = render->ypad;
@@ -511,13 +529,13 @@ gst_text_render_chain (GstPad * pad, GstBuffer * inbuf)
       break;
   }
 
-  if (render->bitmap.buffer) {
+  if (render->text_image) {
     if (render->use_ARGB) {
-      gst_text_renderer_bitmap_to_argb (render, &render->bitmap, data, xpos,
-          xpos + render->bitmap.width, ypos, ypos + render->bitmap.rows);
+      gst_text_renderer_image_to_argb (render, data, xpos, ypos,
+          render->width * 4);
     } else {
-      gst_text_renderer_bitmap_to_ayuv (render, &render->bitmap, data, xpos,
-          xpos + render->bitmap.width, ypos, ypos + render->bitmap.rows);
+      gst_text_renderer_image_to_ayuv (render, data, xpos, ypos,
+          render->width * 4);
     }
   }
 
@@ -536,7 +554,7 @@ gst_text_render_finalize (GObject * object)
 {
   GstTextRender *render = GST_TEXT_RENDER (object);
 
-  g_free (render->bitmap.buffer);
+  g_free (render->text_image);
 
   if (render->layout)
     g_object_unref (render->layout);
@@ -573,7 +591,6 @@ gst_text_render_init (GstTextRender * render, GstTextRenderClass * klass)
       pango_layout_new (GST_TEXT_RENDER_GET_CLASS (render)->pango_context);
   pango_layout_set_alignment (render->layout,
       (PangoAlignment) render->line_align);
-  memset (&render->bitmap, 0, sizeof (render->bitmap));
 
   render->halign = DEFAULT_PROP_HALIGNMENT;
   render->valign = DEFAULT_PROP_VALIGNMENT;
@@ -619,8 +636,9 @@ gst_text_render_set_property (GObject * object, guint prop_id,
         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 11a1eb0..66d37db 100644 (file)
@@ -2,7 +2,7 @@
 #define __GST_TEXT_RENDER_H__
 
 #include <gst/gst.h>
-#include <pango/pangoft2.h>
+#include <pango/pangocairo.h>
 
 G_BEGIN_DECLS
 
@@ -75,8 +75,11 @@ struct _GstTextRender {
     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;
     gboolean              use_ARGB;