ext/cairo/gsttextoverlay.c: Improvements to actually render text as white on black outline on video, including font s...

Original commit message from CVS:
* ext/cairo/gsttextoverlay.c: (gst_textoverlay_render_text),
(gst_text_overlay_blit_1), (gst_text_overlay_blit_sub2x2),
(gst_textoverlay_video_chain), (gst_textoverlay_loop),
(gst_textoverlay_font_init), (gst_textoverlay_init),
(gst_textoverlay_set_property): Improvements to actually
render text as white on black outline on video, including
font selection and horizontal/vertical alignment.  (Ronald's
christmas present)
* ext/cairo/gsttextoverlay.h:
This commit is contained in:
David Schleef 2004-12-28 05:52:03 +00:00
parent 257f1aa294
commit 3065ec53ca
3 changed files with 151 additions and 78 deletions

View file

@ -1,3 +1,15 @@
2004-12-27 David Schleef <ds@schleef.org>
* ext/cairo/gsttextoverlay.c: (gst_textoverlay_render_text),
(gst_text_overlay_blit_1), (gst_text_overlay_blit_sub2x2),
(gst_textoverlay_video_chain), (gst_textoverlay_loop),
(gst_textoverlay_font_init), (gst_textoverlay_init),
(gst_textoverlay_set_property): Improvements to actually
render text as white on black outline on video, including
font selection and horizontal/vertical alignment. (Ronald's
christmas present)
* ext/cairo/gsttextoverlay.h:
2004-12-26 Stephane Loeuillet <stephane.loeuillet@tiscali.fr> 2004-12-26 Stephane Loeuillet <stephane.loeuillet@tiscali.fr>
* ext/ogg/gstogg.c: * ext/ogg/gstogg.c:

View file

@ -213,18 +213,22 @@ gst_textoverlay_render_text (GstTextOverlay * overlay, gchar * text,
{ {
cairo_text_extents_t extents; cairo_text_extents_t extents;
char *string; char *string;
double x, y;
string = g_strdup_printf ("%.*s", textlen, text); string = g_strndup (text, textlen);
if (overlay->pixbuf) if (overlay->text_fill_image)
g_free (overlay->pixbuf); g_free (overlay->text_fill_image);
overlay->pixbuf = g_malloc (4 * overlay->width * overlay->text_height); overlay->text_fill_image =
cairo_set_target_image (overlay->cr, overlay->pixbuf, CAIRO_FORMAT_ARGB32, g_malloc (4 * overlay->width * overlay->text_height);
overlay->width, overlay->text_height, overlay->width * 4); cairo_set_target_image (overlay->cr, overlay->text_fill_image,
CAIRO_FORMAT_ARGB32, overlay->width, overlay->text_height,
overlay->width * 4);
cairo_save (overlay->cr); cairo_save (overlay->cr);
cairo_rectangle (overlay->cr, 0, 0, overlay->width, overlay->text_height); cairo_rectangle (overlay->cr, 0, 0, overlay->width, overlay->text_height);
cairo_set_alpha (overlay->cr, 0.0); cairo_set_rgb_color (overlay->cr, 0, 0, 0);
cairo_set_alpha (overlay->cr, 1.0);
cairo_set_operator (overlay->cr, CAIRO_OPERATOR_SRC); cairo_set_operator (overlay->cr, CAIRO_OPERATOR_SRC);
cairo_fill (overlay->cr); cairo_fill (overlay->cr);
cairo_restore (overlay->cr); cairo_restore (overlay->cr);
@ -232,19 +236,51 @@ gst_textoverlay_render_text (GstTextOverlay * overlay, gchar * text,
cairo_save (overlay->cr); cairo_save (overlay->cr);
cairo_text_extents (overlay->cr, string, &extents); cairo_text_extents (overlay->cr, string, &extents);
cairo_set_rgb_color (overlay->cr, 1, 1, 1); cairo_set_rgb_color (overlay->cr, 1, 1, 1);
cairo_move_to (overlay->cr, 0, overlay->text_height - 2); cairo_set_alpha (overlay->cr, 1.0);
switch (overlay->halign) {
case GST_TEXT_OVERLAY_HALIGN_LEFT:
x = overlay->x0;
break;
case GST_TEXT_OVERLAY_HALIGN_CENTER:
x = overlay->x0 - extents.width / 2;
break;
case GST_TEXT_OVERLAY_HALIGN_RIGHT:
x = overlay->x0 - extents.width;
break;
default:
x = 0;
}
y = overlay->text_height - 2;
cairo_move_to (overlay->cr, x, y);
cairo_show_text (overlay->cr, string); cairo_show_text (overlay->cr, string);
cairo_text_path (overlay->cr, string);
cairo_set_rgb_color (overlay->cr, 0, 0, 0);
cairo_set_line_width (overlay->cr, 1.0);
cairo_stroke (overlay->cr);
g_free (string);
cairo_restore (overlay->cr); cairo_restore (overlay->cr);
overlay->text_width = MIN (extents.width, overlay->width); if (overlay->text_outline_image)
g_free (overlay->text_outline_image);
overlay->text_outline_image =
g_malloc (4 * overlay->width * overlay->text_height);
cairo_set_target_image (overlay->cr, overlay->text_outline_image,
CAIRO_FORMAT_ARGB32, overlay->width, overlay->text_height,
overlay->width * 4);
cairo_save (overlay->cr);
cairo_rectangle (overlay->cr, 0, 0, overlay->width, overlay->text_height);
cairo_set_rgb_color (overlay->cr, 0, 0, 0);
cairo_set_alpha (overlay->cr, 1.0);
cairo_set_operator (overlay->cr, CAIRO_OPERATOR_SRC);
cairo_fill (overlay->cr);
cairo_restore (overlay->cr);
cairo_save (overlay->cr);
cairo_move_to (overlay->cr, x, y);
cairo_set_rgb_color (overlay->cr, 1, 1, 1);
cairo_set_alpha (overlay->cr, 1.0);
cairo_set_line_width (overlay->cr, 1.0);
cairo_text_path (overlay->cr, string);
cairo_stroke (overlay->cr);
cairo_restore (overlay->cr);
g_free (string);
} }
/* static GstPadLinkReturn */ /* static GstPadLinkReturn */
@ -272,36 +308,47 @@ gst_textoverlay_video_sinkconnect (GstPad * pad, const GstCaps * caps)
static void static void
gst_text_overlay_blit_yuv420 (GstTextOverlay * overlay, guchar * pixbuf, gst_text_overlay_blit_1 (GstTextOverlay * overlay, guchar * dest,
int x0, int y0) guchar * text_image, int val)
{ {
int i; int i;
int j; int j;
int x, a, y; int x, a, y;
int y0 = 0;
if (x0 < 0) y = val;
x0 = 0;
if (y0 < 0)
y0 = 0;
GST_LOG ("%d %d %d %d %d %d %p", overlay->text_width, overlay->text_height,
overlay->width, overlay->height, x0, y0, pixbuf);
for (i = 0; i < overlay->text_height; i++) { for (i = 0; i < overlay->text_height; i++) {
for (j = 0; j < overlay->text_width; j++) { for (j = 0; j < overlay->width; j++) {
#if 0 x = dest[(i + y0) * overlay->width + j];
x = pixbuf[(i + y0) * overlay->width + (j + x0)]; a = text_image[4 * (i * overlay->width + j) + 1];
a = overlay->pixbuf[4 * (i * overlay->width + j) + 0]; dest[(i + y0) * overlay->width + j] = (y * a + x * (255 - a)) / 255;
y = overlay->pixbuf[4 * (i * overlay->width + j) + 1];
pixbuf[(i + y0) * overlay->width + (j + x0)] =
(y * a + x * (255 - a)) / 255;
#endif
x = a = 0;
y = overlay->pixbuf[4 * (i * overlay->width + j) + 1];
pixbuf[(i + y0) * overlay->width + (j + x0)] = y;
} }
} }
}
static void
gst_text_overlay_blit_sub2x2 (GstTextOverlay * overlay, guchar * dest,
guchar * text_image, int val)
{
int i;
int j;
int x, a, y;
int y0 = 0;
y = val;
for (i = 0; i < overlay->text_height; i += 2) {
for (j = 0; j < overlay->width; j += 2) {
x = dest[(i / 2 + y0) * (overlay->width / 2) + j / 2];
a = (text_image[4 * (i * overlay->width + j) + 1] +
text_image[4 * (i * overlay->width + j + 1) + 1] +
text_image[4 * ((i + 1) * overlay->width + j) + 1] +
text_image[4 * ((i + 1) * overlay->width + j + 1) + 1] + 2) / 4;
dest[(i / 2 + y0) * (overlay->width / 2) + j / 2] =
(y * a + x * (255 - a)) / 255;
}
}
} }
@ -311,7 +358,7 @@ gst_textoverlay_video_chain (GstPad * pad, GstData * _data)
GstBuffer *buf = GST_BUFFER (_data); GstBuffer *buf = GST_BUFFER (_data);
GstTextOverlay *overlay; GstTextOverlay *overlay;
guchar *pixbuf; guchar *pixbuf;
gint x0, y0; gint y;
g_return_if_fail (pad != NULL); g_return_if_fail (pad != NULL);
g_return_if_fail (GST_IS_PAD (pad)); g_return_if_fail (GST_IS_PAD (pad));
@ -325,32 +372,38 @@ gst_textoverlay_video_chain (GstPad * pad, GstData * _data)
pixbuf = GST_BUFFER_DATA (buf); pixbuf = GST_BUFFER_DATA (buf);
x0 = overlay->x0; y = overlay->y0;
y0 = overlay->y0;
switch (overlay->valign) { switch (overlay->valign) {
case GST_TEXT_OVERLAY_VALIGN_BOTTOM: case GST_TEXT_OVERLAY_VALIGN_BOTTOM:
y0 -= overlay->text_height; y -= overlay->text_height;
break; break;
case GST_TEXT_OVERLAY_VALIGN_BASELINE: case GST_TEXT_OVERLAY_VALIGN_BASELINE:
#define BASELINE 2 #define BASELINE 2
y0 -= (overlay->text_height - BASELINE); y -= (overlay->text_height - BASELINE);
break; break;
case GST_TEXT_OVERLAY_VALIGN_TOP: case GST_TEXT_OVERLAY_VALIGN_TOP:
break; break;
} }
switch (overlay->halign) { gst_text_overlay_blit_1 (overlay,
case GST_TEXT_OVERLAY_HALIGN_LEFT: pixbuf + y * overlay->width, overlay->text_outline_image, 0);
break; gst_text_overlay_blit_sub2x2 (overlay,
case GST_TEXT_OVERLAY_HALIGN_RIGHT: pixbuf + (overlay->height * overlay->width) +
x0 -= overlay->text_width; (y / 2) * overlay->width / 2, overlay->text_outline_image, 128);
break; gst_text_overlay_blit_sub2x2 (overlay, pixbuf +
case GST_TEXT_OVERLAY_HALIGN_CENTER: (overlay->height * overlay->width) +
x0 -= overlay->text_width; (overlay->height * overlay->width) / 4 + (y / 2) * overlay->width / 2,
break; overlay->text_outline_image, 128);
}
gst_text_overlay_blit_yuv420 (overlay, pixbuf, x0, y0); gst_text_overlay_blit_1 (overlay, pixbuf + y * overlay->width,
overlay->text_fill_image, 255);
gst_text_overlay_blit_sub2x2 (overlay,
pixbuf + (overlay->height * overlay->width) +
(y / 2) * overlay->width / 2, overlay->text_fill_image, 128);
gst_text_overlay_blit_sub2x2 (overlay,
pixbuf + (overlay->height * overlay->width) +
(overlay->height * overlay->width) / 4 + (y / 2) * overlay->width / 2,
overlay->text_fill_image, 128);
gst_pad_push (overlay->srcpad, GST_DATA (buf)); gst_pad_push (overlay->srcpad, GST_DATA (buf));
} }
@ -443,6 +496,22 @@ gst_textoverlay_loop (GstElement * element)
gst_textoverlay_video_chain (overlay->srcpad, GST_DATA (video_frame)); gst_textoverlay_video_chain (overlay->srcpad, GST_DATA (video_frame));
} }
static void
gst_textoverlay_font_init (GstTextOverlay * overlay)
{
cairo_font_extents_t font_extents;
cairo_select_font (overlay->cr, overlay->font, overlay->slant,
overlay->weight);
cairo_scale_font (overlay->cr, overlay->scale);
cairo_current_font_extents (overlay->cr, &font_extents);
overlay->text_height = font_extents.height;
if (overlay->text_height & 1)
overlay->text_height++;
overlay->need_render = TRUE;
}
static GstElementStateReturn static GstElementStateReturn
gst_textoverlay_change_state (GstElement * element) gst_textoverlay_change_state (GstElement * element)
@ -480,8 +549,6 @@ gst_textoverlay_finalize (GObject * object)
static void static void
gst_textoverlay_init (GstTextOverlay * overlay) gst_textoverlay_init (GstTextOverlay * overlay)
{ {
cairo_font_extents_t font_extents;
/* video sink */ /* video sink */
overlay->video_sinkpad = overlay->video_sinkpad =
gst_pad_new_from_template (gst_static_pad_template_get gst_pad_new_from_template (gst_static_pad_template_get
@ -511,16 +578,15 @@ gst_textoverlay_init (GstTextOverlay * overlay)
overlay->default_text = g_strdup (""); overlay->default_text = g_strdup ("");
overlay->need_render = TRUE; overlay->need_render = TRUE;
cairo_select_font (overlay->cr, "sans", 0, 0); overlay->font = g_strdup ("sans");
cairo_scale_font (overlay->cr, 50); overlay->slant = CAIRO_FONT_SLANT_NORMAL;
overlay->weight = CAIRO_FONT_WEIGHT_NORMAL;
cairo_current_font_extents (overlay->cr, &font_extents); overlay->scale = 20;
overlay->text_height = font_extents.height; gst_textoverlay_font_init (overlay);
gst_element_set_loop_function (GST_ELEMENT (overlay), gst_textoverlay_loop); gst_element_set_loop_function (GST_ELEMENT (overlay), gst_textoverlay_loop);
} }
static void static void
gst_textoverlay_set_property (GObject * object, guint prop_id, gst_textoverlay_set_property (GObject * object, guint prop_id,
const GValue * value, GParamSpec * pspec) const GValue * value, GParamSpec * pspec)
@ -551,6 +617,7 @@ gst_textoverlay_set_property (GObject * object, guint prop_id,
else else
g_warning ("Invalid 'valign' property value: %s", g_warning ("Invalid 'valign' property value: %s",
g_value_get_string (value)); g_value_get_string (value));
overlay->need_render = TRUE;
break; break;
case ARG_HALIGN: case ARG_HALIGN:
@ -563,6 +630,7 @@ gst_textoverlay_set_property (GObject * object, guint prop_id,
else else
g_warning ("Invalid 'halign' property value: %s", g_warning ("Invalid 'halign' property value: %s",
g_value_get_string (value)); g_value_get_string (value));
overlay->need_render = TRUE;
break; break;
case ARG_X0: case ARG_X0:
@ -574,22 +642,11 @@ gst_textoverlay_set_property (GObject * object, guint prop_id,
break; break;
case ARG_FONT_DESC: case ARG_FONT_DESC:
{ if (overlay->font)
#if 0 g_free (overlay->font);
PangoFontDescription *desc; overlay->font = g_strdup (g_value_get_string (value));
gst_textoverlay_font_init (overlay);
desc = pango_font_description_from_string (g_value_get_string (value));
if (desc) {
g_message ("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
g_warning ("font description parse failed: %s",
g_value_get_string (value));
#endif
break; break;
}
default: default:
break; break;

View file

@ -54,14 +54,18 @@ struct _GstTextOverlay {
gchar *default_text; gchar *default_text;
cairo_t *cr; cairo_t *cr;
guchar *pixbuf; guchar *text_fill_image;
int text_width; guchar *text_outline_image;
int text_height; int text_height;
GstBuffer *current_buffer; GstBuffer *current_buffer;
GstBuffer *next_buffer; GstBuffer *next_buffer;
gboolean need_render; gboolean need_render;
gchar *font;
int slant;
int weight;
double scale;
}; };
struct _GstTextOverlayClass { struct _GstTextOverlayClass {