From 3065ec53ca028e3dda508227ac38cfd50faec078 Mon Sep 17 00:00:00 2001 From: David Schleef Date: Tue, 28 Dec 2004 05:52:03 +0000 Subject: [PATCH] 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: --- ChangeLog | 12 +++ ext/cairo/gsttextoverlay.c | 209 +++++++++++++++++++++++-------------- ext/cairo/gsttextoverlay.h | 8 +- 3 files changed, 151 insertions(+), 78 deletions(-) diff --git a/ChangeLog b/ChangeLog index 87580b0c88..54473e5ea4 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,15 @@ +2004-12-27 David Schleef + + * 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 * ext/ogg/gstogg.c: diff --git a/ext/cairo/gsttextoverlay.c b/ext/cairo/gsttextoverlay.c index 4ca302ee24..b2c1e8753b 100644 --- a/ext/cairo/gsttextoverlay.c +++ b/ext/cairo/gsttextoverlay.c @@ -213,18 +213,22 @@ gst_textoverlay_render_text (GstTextOverlay * overlay, gchar * text, { cairo_text_extents_t extents; char *string; + double x, y; - string = g_strdup_printf ("%.*s", textlen, text); + string = g_strndup (text, textlen); - if (overlay->pixbuf) - g_free (overlay->pixbuf); - overlay->pixbuf = g_malloc (4 * overlay->width * overlay->text_height); - cairo_set_target_image (overlay->cr, overlay->pixbuf, CAIRO_FORMAT_ARGB32, - overlay->width, overlay->text_height, overlay->width * 4); + if (overlay->text_fill_image) + g_free (overlay->text_fill_image); + overlay->text_fill_image = + g_malloc (4 * overlay->width * overlay->text_height); + 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_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_fill (overlay->cr); cairo_restore (overlay->cr); @@ -232,19 +236,51 @@ gst_textoverlay_render_text (GstTextOverlay * overlay, gchar * text, cairo_save (overlay->cr); cairo_text_extents (overlay->cr, string, &extents); 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_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); - 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 */ @@ -272,36 +308,47 @@ gst_textoverlay_video_sinkconnect (GstPad * pad, const GstCaps * caps) static void -gst_text_overlay_blit_yuv420 (GstTextOverlay * overlay, guchar * pixbuf, - int x0, int y0) +gst_text_overlay_blit_1 (GstTextOverlay * overlay, guchar * dest, + guchar * text_image, int val) { int i; int j; int x, a, y; + int y0 = 0; - if (x0 < 0) - 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); + y = val; for (i = 0; i < overlay->text_height; i++) { - for (j = 0; j < overlay->text_width; j++) { -#if 0 - x = pixbuf[(i + y0) * overlay->width + (j + x0)]; - a = overlay->pixbuf[4 * (i * overlay->width + j) + 0]; - 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; + for (j = 0; j < overlay->width; j++) { + x = dest[(i + y0) * overlay->width + j]; + a = text_image[4 * (i * overlay->width + j) + 1]; + dest[(i + y0) * overlay->width + j] = (y * a + x * (255 - a)) / 255; } } +} +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); GstTextOverlay *overlay; guchar *pixbuf; - gint x0, y0; + gint y; g_return_if_fail (pad != NULL); 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); - x0 = overlay->x0; - y0 = overlay->y0; + y = overlay->y0; switch (overlay->valign) { case GST_TEXT_OVERLAY_VALIGN_BOTTOM: - y0 -= overlay->text_height; + y -= overlay->text_height; break; case GST_TEXT_OVERLAY_VALIGN_BASELINE: #define BASELINE 2 - y0 -= (overlay->text_height - BASELINE); + y -= (overlay->text_height - BASELINE); break; case GST_TEXT_OVERLAY_VALIGN_TOP: break; } - switch (overlay->halign) { - case GST_TEXT_OVERLAY_HALIGN_LEFT: - break; - case GST_TEXT_OVERLAY_HALIGN_RIGHT: - x0 -= overlay->text_width; - break; - case GST_TEXT_OVERLAY_HALIGN_CENTER: - x0 -= overlay->text_width; - break; - } + gst_text_overlay_blit_1 (overlay, + pixbuf + y * overlay->width, overlay->text_outline_image, 0); + gst_text_overlay_blit_sub2x2 (overlay, + pixbuf + (overlay->height * overlay->width) + + (y / 2) * overlay->width / 2, overlay->text_outline_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_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)); } @@ -443,6 +496,22 @@ gst_textoverlay_loop (GstElement * element) 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 gst_textoverlay_change_state (GstElement * element) @@ -480,8 +549,6 @@ gst_textoverlay_finalize (GObject * object) static void gst_textoverlay_init (GstTextOverlay * overlay) { - cairo_font_extents_t font_extents; - /* video sink */ overlay->video_sinkpad = 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->need_render = TRUE; - cairo_select_font (overlay->cr, "sans", 0, 0); - cairo_scale_font (overlay->cr, 50); - - cairo_current_font_extents (overlay->cr, &font_extents); - overlay->text_height = font_extents.height; + overlay->font = g_strdup ("sans"); + overlay->slant = CAIRO_FONT_SLANT_NORMAL; + overlay->weight = CAIRO_FONT_WEIGHT_NORMAL; + overlay->scale = 20; + gst_textoverlay_font_init (overlay); gst_element_set_loop_function (GST_ELEMENT (overlay), gst_textoverlay_loop); } - static void gst_textoverlay_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec) @@ -551,6 +617,7 @@ gst_textoverlay_set_property (GObject * object, guint prop_id, else g_warning ("Invalid 'valign' property value: %s", g_value_get_string (value)); + overlay->need_render = TRUE; break; case ARG_HALIGN: @@ -563,6 +630,7 @@ gst_textoverlay_set_property (GObject * object, guint prop_id, else g_warning ("Invalid 'halign' property value: %s", g_value_get_string (value)); + overlay->need_render = TRUE; break; case ARG_X0: @@ -574,22 +642,11 @@ gst_textoverlay_set_property (GObject * object, guint prop_id, break; case ARG_FONT_DESC: - { -#if 0 - PangoFontDescription *desc; - - 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 + if (overlay->font) + g_free (overlay->font); + overlay->font = g_strdup (g_value_get_string (value)); + gst_textoverlay_font_init (overlay); break; - } default: break; diff --git a/ext/cairo/gsttextoverlay.h b/ext/cairo/gsttextoverlay.h index ff53913199..00862f786f 100644 --- a/ext/cairo/gsttextoverlay.h +++ b/ext/cairo/gsttextoverlay.h @@ -54,14 +54,18 @@ struct _GstTextOverlay { gchar *default_text; cairo_t *cr; - guchar *pixbuf; - int text_width; + guchar *text_fill_image; + guchar *text_outline_image; int text_height; GstBuffer *current_buffer; GstBuffer *next_buffer; gboolean need_render; + gchar *font; + int slant; + int weight; + double scale; }; struct _GstTextOverlayClass {