mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-12-25 09:40:37 +00:00
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:
parent
257f1aa294
commit
3065ec53ca
3 changed files with 151 additions and 78 deletions
12
ChangeLog
12
ChangeLog
|
@ -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:
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
Loading…
Reference in a new issue