pango: Use pango-cairo instead of pango-ft2
pango-cairo will always use the native font rendering backend of the platform and provides better results. Fixes bug #340887.
This commit is contained in:
parent
76571840ef
commit
7608c31516
@ -4,6 +4,7 @@
|
|||||||
* Copyright (C) <2006> Julien Moutte <julien@moutte.net>
|
* Copyright (C) <2006> Julien Moutte <julien@moutte.net>
|
||||||
* Copyright (C) <2006> Zeeshan Ali <zeeshan.ali@nokia.com>
|
* Copyright (C) <2006> Zeeshan Ali <zeeshan.ali@nokia.com>
|
||||||
* Copyright (C) <2006-2008> Tim-Philipp Müller <tim centricular net>
|
* 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
|
* This library is free software; you can redistribute it and/or
|
||||||
* modify it under the terms of the GNU Library General Public
|
* modify it under the terms of the GNU Library General Public
|
||||||
@ -80,6 +81,7 @@
|
|||||||
#include "gsttimeoverlay.h"
|
#include "gsttimeoverlay.h"
|
||||||
#include "gstclockoverlay.h"
|
#include "gstclockoverlay.h"
|
||||||
#include "gsttextrender.h"
|
#include "gsttextrender.h"
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
/* FIXME:
|
/* FIXME:
|
||||||
* - use proper strides and offset for I420
|
* - use proper strides and offset for I420
|
||||||
@ -113,10 +115,38 @@ GST_ELEMENT_DETAILS ("Text overlay",
|
|||||||
#define DEFAULT_PROP_SILENT FALSE
|
#define DEFAULT_PROP_SILENT FALSE
|
||||||
#define DEFAULT_PROP_LINE_ALIGNMENT GST_TEXT_OVERLAY_LINE_ALIGN_CENTER
|
#define DEFAULT_PROP_LINE_ALIGNMENT GST_TEXT_OVERLAY_LINE_ALIGN_CENTER
|
||||||
#define DEFAULT_PROP_WAIT_TEXT TRUE
|
#define DEFAULT_PROP_WAIT_TEXT TRUE
|
||||||
|
#define DEFAULT_PROP_AUTO_ADJUST_SIZE TRUE
|
||||||
|
#define DEFAULT_PROP_VERTICAL_RENDER FALSE
|
||||||
|
|
||||||
/* make a property of me */
|
/* make a property of me */
|
||||||
#define DEFAULT_SHADING_VALUE -80
|
#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
|
enum
|
||||||
{
|
{
|
||||||
PROP_0,
|
PROP_0,
|
||||||
@ -135,6 +165,8 @@ enum
|
|||||||
PROP_SILENT,
|
PROP_SILENT,
|
||||||
PROP_LINE_ALIGNMENT,
|
PROP_LINE_ALIGNMENT,
|
||||||
PROP_WAIT_TEXT,
|
PROP_WAIT_TEXT,
|
||||||
|
PROP_AUTO_ADJUST_SIZE,
|
||||||
|
PROP_VERTICAL_RENDER,
|
||||||
PROP_LAST
|
PROP_LAST
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -265,12 +297,15 @@ static GstPadLinkReturn gst_text_overlay_text_pad_link (GstPad * pad,
|
|||||||
GstPad * peer);
|
GstPad * peer);
|
||||||
static void gst_text_overlay_text_pad_unlink (GstPad * pad);
|
static void gst_text_overlay_text_pad_unlink (GstPad * pad);
|
||||||
static void gst_text_overlay_pop_text (GstTextOverlay * overlay);
|
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_finalize (GObject * object);
|
||||||
static void gst_text_overlay_set_property (GObject * object, guint prop_id,
|
static void gst_text_overlay_set_property (GObject * object, guint prop_id,
|
||||||
const GValue * value, GParamSpec * pspec);
|
const GValue * value, GParamSpec * pspec);
|
||||||
static void gst_text_overlay_get_property (GObject * object, guint prop_id,
|
static void gst_text_overlay_get_property (GObject * object, guint prop_id,
|
||||||
GValue * value, GParamSpec * pspec);
|
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)
|
GST_BOILERPLATE (GstTextOverlay, gst_text_overlay, GstElement, GST_TYPE_ELEMENT)
|
||||||
|
|
||||||
@ -304,6 +339,7 @@ gst_text_overlay_class_init (GstTextOverlayClass * klass)
|
|||||||
{
|
{
|
||||||
GObjectClass *gobject_class;
|
GObjectClass *gobject_class;
|
||||||
GstElementClass *gstelement_class;
|
GstElementClass *gstelement_class;
|
||||||
|
PangoFontMap *fontmap;
|
||||||
|
|
||||||
gobject_class = (GObjectClass *) klass;
|
gobject_class = (GObjectClass *) klass;
|
||||||
gstelement_class = (GstElementClass *) klass;
|
gstelement_class = (GstElementClass *) klass;
|
||||||
@ -316,7 +352,9 @@ gst_text_overlay_class_init (GstTextOverlayClass * klass)
|
|||||||
GST_DEBUG_FUNCPTR (gst_text_overlay_change_state);
|
GST_DEBUG_FUNCPTR (gst_text_overlay_change_state);
|
||||||
|
|
||||||
klass->get_text = gst_text_overlay_get_text;
|
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_object_class_install_property (G_OBJECT_CLASS (klass), PROP_TEXT,
|
||||||
g_param_spec_string ("text", "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",
|
g_param_spec_boolean ("wait-text", "Wait Text",
|
||||||
"Whether to wait for subtitles",
|
"Whether to wait for subtitles",
|
||||||
DEFAULT_PROP_WAIT_TEXT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
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
|
static void
|
||||||
@ -416,7 +464,11 @@ gst_text_overlay_finalize (GObject * object)
|
|||||||
GstTextOverlay *overlay = GST_TEXT_OVERLAY (object);
|
GstTextOverlay *overlay = GST_TEXT_OVERLAY (object);
|
||||||
|
|
||||||
g_free (overlay->default_text);
|
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) {
|
if (overlay->layout) {
|
||||||
g_object_unref (overlay->layout);
|
g_object_unref (overlay->layout);
|
||||||
@ -445,6 +497,7 @@ static void
|
|||||||
gst_text_overlay_init (GstTextOverlay * overlay, GstTextOverlayClass * klass)
|
gst_text_overlay_init (GstTextOverlay * overlay, GstTextOverlayClass * klass)
|
||||||
{
|
{
|
||||||
GstPadTemplate *template;
|
GstPadTemplate *template;
|
||||||
|
PangoFontDescription *desc;
|
||||||
|
|
||||||
/* video sink */
|
/* video sink */
|
||||||
template = gst_static_pad_template_get (&video_sink_template_factory);
|
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->line_align = DEFAULT_PROP_LINE_ALIGNMENT;
|
||||||
overlay->layout =
|
overlay->layout =
|
||||||
pango_layout_new (GST_TEXT_OVERLAY_GET_CLASS (overlay)->pango_context);
|
pango_layout_new (GST_TEXT_OVERLAY_GET_CLASS (overlay)->pango_context);
|
||||||
pango_layout_set_alignment (overlay->layout,
|
desc =
|
||||||
(PangoAlignment) overlay->line_align);
|
pango_context_get_font_description (GST_TEXT_OVERLAY_GET_CLASS
|
||||||
memset (&overlay->bitmap, 0, sizeof (overlay->bitmap));
|
(overlay)->pango_context);
|
||||||
|
gst_text_overlay_adjust_values_with_fontdesc (overlay, desc);
|
||||||
|
|
||||||
overlay->halign = DEFAULT_PROP_HALIGNMENT;
|
overlay->halign = DEFAULT_PROP_HALIGNMENT;
|
||||||
overlay->valign = DEFAULT_PROP_VALIGNMENT;
|
overlay->valign = DEFAULT_PROP_VALIGNMENT;
|
||||||
@ -508,9 +562,13 @@ gst_text_overlay_init (GstTextOverlay * overlay, GstTextOverlayClass * klass)
|
|||||||
overlay->shading_value = DEFAULT_SHADING_VALUE;
|
overlay->shading_value = DEFAULT_SHADING_VALUE;
|
||||||
overlay->silent = DEFAULT_PROP_SILENT;
|
overlay->silent = DEFAULT_PROP_SILENT;
|
||||||
overlay->wait_text = DEFAULT_PROP_WAIT_TEXT;
|
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->default_text = g_strdup (DEFAULT_PROP_TEXT);
|
||||||
overlay->need_render = TRUE;
|
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_n = 0;
|
||||||
overlay->fps_d = 1;
|
overlay->fps_d = 1;
|
||||||
@ -534,13 +592,48 @@ gst_text_overlay_update_wrap_mode (GstTextOverlay * overlay)
|
|||||||
GST_DEBUG_OBJECT (overlay, "Set wrap mode NONE");
|
GST_DEBUG_OBJECT (overlay, "Set wrap mode NONE");
|
||||||
pango_layout_set_width (overlay->layout, -1);
|
pango_layout_set_width (overlay->layout, -1);
|
||||||
} else {
|
} 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 layout width %d", overlay->width);
|
||||||
GST_DEBUG_OBJECT (overlay, "Set wrap mode %d", overlay->wrap_mode);
|
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);
|
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
|
static gboolean
|
||||||
gst_text_overlay_setcaps_txt (GstPad * pad, GstCaps * caps)
|
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) {
|
if (desc) {
|
||||||
GST_LOG_OBJECT (overlay, "font description set: %s", fontdesc_str);
|
GST_LOG_OBJECT (overlay, "font description set: %s", fontdesc_str);
|
||||||
pango_layout_set_font_description (overlay->layout, desc);
|
pango_layout_set_font_description (overlay->layout, desc);
|
||||||
|
gst_text_overlay_adjust_values_with_fontdesc (overlay, desc);
|
||||||
pango_font_description_free (desc);
|
pango_font_description_free (desc);
|
||||||
} else {
|
} else {
|
||||||
GST_WARNING_OBJECT (overlay, "font description parse failed: %s",
|
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:
|
case PROP_WAIT_TEXT:
|
||||||
overlay->wait_text = g_value_get_boolean (value);
|
overlay->wait_text = g_value_get_boolean (value);
|
||||||
break;
|
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:
|
default:
|
||||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||||
break;
|
break;
|
||||||
@ -748,6 +855,16 @@ gst_text_overlay_get_property (GObject * object, guint prop_id,
|
|||||||
case PROP_WAIT_TEXT:
|
case PROP_WAIT_TEXT:
|
||||||
g_value_set_boolean (value, overlay->wait_text);
|
g_value_set_boolean (value, overlay->wait_text);
|
||||||
break;
|
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:
|
default:
|
||||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||||
break;
|
break;
|
||||||
@ -854,6 +971,285 @@ gst_text_overlay_getcaps (GstPad * pad)
|
|||||||
return caps;
|
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_XPAD 6
|
||||||
#define BOX_YPAD 6
|
#define BOX_YPAD 6
|
||||||
|
|
||||||
@ -915,21 +1311,12 @@ gst_text_overlay_shade_UYVY_y (GstTextOverlay * overlay, guchar * dest,
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
static inline void
|
static inline void
|
||||||
gst_text_overlay_blit_I420 (GstTextOverlay * overlay, FT_Bitmap * bitmap,
|
gst_text_overlay_blit_I420 (GstTextOverlay * overlay,
|
||||||
guint8 * yuv_pixels, gint x0, gint y0)
|
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 y_stride, u_stride, v_stride;
|
||||||
int u_offset, v_offset;
|
int u_offset, v_offset;
|
||||||
int h, w;
|
int h, w;
|
||||||
guint8 v;
|
|
||||||
|
|
||||||
w = overlay->width;
|
w = overlay->width;
|
||||||
h = overlay->height;
|
h = overlay->height;
|
||||||
@ -942,177 +1329,90 @@ gst_text_overlay_blit_I420 (GstTextOverlay * overlay, FT_Bitmap * bitmap,
|
|||||||
v_offset =
|
v_offset =
|
||||||
gst_video_format_get_component_offset (GST_VIDEO_FORMAT_I420, 2, w, h);
|
gst_video_format_get_component_offset (GST_VIDEO_FORMAT_I420, 2, w, h);
|
||||||
|
|
||||||
/*
|
gst_text_overlay_blit_1 (overlay, yuv_pixels, xpos, ypos, overlay->text_image,
|
||||||
if (x0 < 0 && abs (x0) < bitmap_width) {
|
y_stride);
|
||||||
bitmap_x0 = abs (x0);
|
gst_text_overlay_blit_sub2x2cbcr (overlay, yuv_pixels + u_offset,
|
||||||
x0 = 0;
|
yuv_pixels + v_offset, xpos, ypos, overlay->text_image, u_stride,
|
||||||
}
|
v_stride);
|
||||||
*/
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void
|
static inline void
|
||||||
gst_text_overlay_blit_UYVY (GstTextOverlay * overlay, FT_Bitmap * bitmap,
|
gst_text_overlay_blit_UYVY (GstTextOverlay * overlay,
|
||||||
guint8 * yuv_pixels, gint x0, gint y0)
|
guint8 * yuv_pixels, gint xpos, gint ypos)
|
||||||
{
|
{
|
||||||
int y; /* text bitmap coordinates */
|
int a0, r0, g0, b0;
|
||||||
int x1, y1; /* video buffer coordinates */
|
int a1, r1, g1, b1;
|
||||||
guint8 *p, *bitp;
|
int y0, y1, u, v;
|
||||||
int video_width, video_height;
|
int i, j;
|
||||||
int bitmap_x0 = 0; //x0 < 1 ? -(x0 - 1) : 1; /* 1 pixel border */
|
int h, w;
|
||||||
int bitmap_y0 = y0 < 1 ? -(y0 - 1) : 1; /* 1 pixel border */
|
guchar *pimage, *dest;
|
||||||
int bitmap_width = bitmap->width - bitmap_x0;
|
|
||||||
int bitmap_height = bitmap->rows - bitmap_y0;
|
|
||||||
|
|
||||||
video_width =
|
w = overlay->image_width - 2;
|
||||||
gst_video_format_get_row_stride (GST_VIDEO_FORMAT_UYVY, 0,
|
h = overlay->image_height - 2;
|
||||||
overlay->width);
|
|
||||||
video_height = overlay->height;
|
|
||||||
|
|
||||||
/*g_debug ("bliting bitmap(%d, %d) on yuv (%d,%d) at %d,%d",
|
if (xpos < 0) {
|
||||||
bitmap_width, bitmap_height,
|
xpos = 0;
|
||||||
video_width, video_height,
|
}
|
||||||
x0, y0); */
|
|
||||||
|
|
||||||
if (x0 + bitmap_x0 + bitmap_width > overlay->width - 1) /* 1 pixel border */
|
if (xpos + w > overlay->width) {
|
||||||
bitmap_width -= x0 + bitmap_x0 + bitmap_width - overlay->width + 1;
|
w = overlay->width - xpos;
|
||||||
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;
|
if (ypos + h > overlay->height) {
|
||||||
y1 = y0 + bitmap_y0;
|
h = overlay->height - ypos;
|
||||||
|
}
|
||||||
|
|
||||||
/* draw an outline around the text */
|
for (i = 0; i < h; i++) {
|
||||||
for (y = bitmap_y0; y < bitmap_y0 + bitmap_height; y++) {
|
pimage = overlay->text_image + i * overlay->image_width * 4;
|
||||||
int n;
|
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++;
|
||||||
|
|
||||||
bitp = bitmap->buffer + (y * bitmap->pitch) + bitmap_x0;
|
b1 = *pimage++;
|
||||||
p = yuv_pixels + (y0 + y) * video_width + (x1 * 2) + 1;
|
g1 = *pimage++;
|
||||||
for (n = bitmap_width; n > 0; --n) {
|
r1 = *pimage++;
|
||||||
if (*bitp) {
|
a1 = *pimage++;
|
||||||
*(p + 2) = CLAMP (*(p + 2) - *bitp, 0, 255);
|
|
||||||
*(p - 2) = CLAMP (*(p - 2) - *bitp, 0, 255);
|
a0 += a1 + 2;
|
||||||
*(p - video_width) = CLAMP (*(p - video_width) - *bitp, 0, 255);
|
a0 /= 2;
|
||||||
*(p + video_width) = CLAMP (*(p + video_width) - *bitp, 0, 255);
|
if (a0 == 0) {
|
||||||
|
dest += 4;
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
p += 2;
|
|
||||||
bitp++;
|
COMP_Y (y0, r0, g0, b0);
|
||||||
|
COMP_Y (y1, r1, g1, b1);
|
||||||
|
|
||||||
|
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++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* now blit 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 = *bitp;
|
|
||||||
*(p - 1) = 0x80;
|
|
||||||
}
|
|
||||||
p += 2;
|
|
||||||
bitp++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
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
|
static void
|
||||||
gst_text_overlay_render_text (GstTextOverlay * overlay,
|
gst_text_overlay_render_text (GstTextOverlay * overlay,
|
||||||
const gchar * text, gint textlen)
|
const gchar * text, gint textlen)
|
||||||
{
|
{
|
||||||
PangoRectangle ink_rect, logical_rect;
|
|
||||||
gchar *string;
|
gchar *string;
|
||||||
|
|
||||||
if (!overlay->need_render) {
|
if (!overlay->need_render) {
|
||||||
@ -1136,13 +1436,7 @@ gst_text_overlay_render_text (GstTextOverlay * overlay,
|
|||||||
/* FIXME: should we check for UTF-8 here? */
|
/* FIXME: should we check for UTF-8 here? */
|
||||||
|
|
||||||
GST_DEBUG ("Rendering '%s'", string);
|
GST_DEBUG ("Rendering '%s'", string);
|
||||||
pango_layout_set_markup (overlay->layout, string, textlen);
|
gst_text_overlay_render_pangocairo (overlay, 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;
|
|
||||||
|
|
||||||
g_free (string);
|
g_free (string);
|
||||||
|
|
||||||
@ -1153,31 +1447,45 @@ static GstFlowReturn
|
|||||||
gst_text_overlay_push_frame (GstTextOverlay * overlay, GstBuffer * video_frame)
|
gst_text_overlay_push_frame (GstTextOverlay * overlay, GstBuffer * video_frame)
|
||||||
{
|
{
|
||||||
gint xpos, ypos;
|
gint xpos, ypos;
|
||||||
|
gint width, height;
|
||||||
|
GstTextOverlayVAlign valign;
|
||||||
|
GstTextOverlayHAlign halign;
|
||||||
|
|
||||||
video_frame = gst_buffer_make_writable (video_frame);
|
width = overlay->image_width;
|
||||||
|
height = overlay->image_height;
|
||||||
|
|
||||||
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:
|
case GST_TEXT_OVERLAY_HALIGN_LEFT:
|
||||||
xpos = overlay->xpad;
|
xpos = overlay->xpad;
|
||||||
break;
|
break;
|
||||||
case GST_TEXT_OVERLAY_HALIGN_CENTER:
|
case GST_TEXT_OVERLAY_HALIGN_CENTER:
|
||||||
xpos = (overlay->width - overlay->bitmap.width) / 2;
|
xpos = (overlay->width - width) / 2;
|
||||||
break;
|
break;
|
||||||
case GST_TEXT_OVERLAY_HALIGN_RIGHT:
|
case GST_TEXT_OVERLAY_HALIGN_RIGHT:
|
||||||
xpos = overlay->width - overlay->bitmap.width - overlay->xpad;
|
xpos = overlay->width - width - overlay->xpad;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
xpos = 0;
|
xpos = 0;
|
||||||
}
|
}
|
||||||
xpos += overlay->deltax;
|
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:
|
case GST_TEXT_OVERLAY_VALIGN_BOTTOM:
|
||||||
ypos = overlay->height - overlay->bitmap.rows - overlay->ypad;
|
ypos = overlay->height - height - overlay->ypad;
|
||||||
break;
|
break;
|
||||||
case GST_TEXT_OVERLAY_VALIGN_BASELINE:
|
case GST_TEXT_OVERLAY_VALIGN_BASELINE:
|
||||||
ypos = overlay->height - (overlay->bitmap.rows + overlay->ypad);
|
ypos = overlay->height - (height + overlay->ypad);
|
||||||
break;
|
break;
|
||||||
case GST_TEXT_OVERLAY_VALIGN_TOP:
|
case GST_TEXT_OVERLAY_VALIGN_TOP:
|
||||||
ypos = overlay->ypad;
|
ypos = overlay->ypad;
|
||||||
@ -1188,40 +1496,23 @@ gst_text_overlay_push_frame (GstTextOverlay * overlay, GstBuffer * video_frame)
|
|||||||
}
|
}
|
||||||
ypos += overlay->deltay;
|
ypos += overlay->deltay;
|
||||||
|
|
||||||
/* shaded background box */
|
if (ypos < 0)
|
||||||
if (overlay->want_shading) {
|
ypos = 0;
|
||||||
|
|
||||||
|
if (overlay->text_image) {
|
||||||
switch (overlay->format) {
|
switch (overlay->format) {
|
||||||
case GST_MAKE_FOURCC ('I', '4', '2', '0'):
|
case GST_MAKE_FOURCC ('I', '4', '2', '0'):
|
||||||
gst_text_overlay_shade_I420_y (overlay,
|
gst_text_overlay_blit_I420 (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 (overlay->bitmap.buffer) {
|
|
||||||
switch (overlay->format) {
|
|
||||||
case GST_MAKE_FOURCC ('I', '4', '2', '0'):
|
|
||||||
gst_text_overlay_blit_I420 (overlay, &overlay->bitmap,
|
|
||||||
GST_BUFFER_DATA (video_frame), xpos, ypos);
|
GST_BUFFER_DATA (video_frame), xpos, ypos);
|
||||||
break;
|
break;
|
||||||
case GST_MAKE_FOURCC ('U', 'Y', 'V', 'Y'):
|
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);
|
GST_BUFFER_DATA (video_frame), xpos, ypos);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
g_assert_not_reached ();
|
g_assert_not_reached ();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return gst_pad_push (overlay->srcpad, video_frame);
|
return gst_pad_push (overlay->srcpad, video_frame);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
#define __GST_TEXT_OVERLAY_H__
|
#define __GST_TEXT_OVERLAY_H__
|
||||||
|
|
||||||
#include <gst/gst.h>
|
#include <gst/gst.h>
|
||||||
#include <pango/pangoft2.h>
|
#include <pango/pangocairo.h>
|
||||||
|
|
||||||
G_BEGIN_DECLS
|
G_BEGIN_DECLS
|
||||||
|
|
||||||
@ -126,15 +126,20 @@ struct _GstTextOverlay {
|
|||||||
gboolean wait_text;
|
gboolean wait_text;
|
||||||
|
|
||||||
PangoLayout *layout;
|
PangoLayout *layout;
|
||||||
FT_Bitmap bitmap;
|
gdouble shadow_offset;
|
||||||
gint bitmap_buffer_size;
|
gdouble outline_offset;
|
||||||
|
guchar *text_image;
|
||||||
|
gint image_width;
|
||||||
|
gint image_height;
|
||||||
gint baseline_y;
|
gint baseline_y;
|
||||||
|
|
||||||
|
gboolean auto_adjust_size;
|
||||||
gboolean need_render;
|
gboolean need_render;
|
||||||
|
|
||||||
gint shading_value; /* for timeoverlay subclass */
|
gint shading_value; /* for timeoverlay subclass */
|
||||||
|
|
||||||
gboolean have_pango_markup;
|
gboolean have_pango_markup;
|
||||||
|
gboolean use_vertical_render;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct _GstTextOverlayClass {
|
struct _GstTextOverlayClass {
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
/* GStreamer
|
/* GStreamer
|
||||||
* Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
|
* Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
|
||||||
* Copyright (C) <2003> David Schleef <ds@schleef.org>
|
* 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
|
* This library is free software; you can redistribute it and/or
|
||||||
* modify it under the terms of the GNU Library General Public
|
* modify it under the terms of the GNU Library General Public
|
||||||
@ -46,10 +47,13 @@
|
|||||||
#include <gst/video/video.h>
|
#include <gst/video/video.h>
|
||||||
|
|
||||||
#include "gsttextrender.h"
|
#include "gsttextrender.h"
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
GST_DEBUG_CATEGORY_EXTERN (pango_debug);
|
GST_DEBUG_CATEGORY_EXTERN (pango_debug);
|
||||||
#define GST_CAT_DEFAULT pango_debug
|
#define GST_CAT_DEFAULT pango_debug
|
||||||
|
|
||||||
|
#define MINIMUM_OUTLINE_OFFSET 1.0
|
||||||
|
|
||||||
static const GstElementDetails text_render_details =
|
static const GstElementDetails text_render_details =
|
||||||
GST_ELEMENT_DETAILS ("Text renderer",
|
GST_ELEMENT_DETAILS ("Text renderer",
|
||||||
"Filter/Editor/Video",
|
"Filter/Editor/Video",
|
||||||
@ -150,6 +154,9 @@ gst_text_render_line_align_get_type (void)
|
|||||||
return text_render_line_align_type;
|
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);
|
GST_BOILERPLATE (GstTextRender, gst_text_render, GstElement, GST_TYPE_ELEMENT);
|
||||||
|
|
||||||
static void gst_text_render_finalize (GObject * object);
|
static void gst_text_render_finalize (GObject * object);
|
||||||
@ -176,6 +183,7 @@ gst_text_render_class_init (GstTextRenderClass * klass)
|
|||||||
{
|
{
|
||||||
GObjectClass *gobject_class;
|
GObjectClass *gobject_class;
|
||||||
GstElementClass *gstelement_class;
|
GstElementClass *gstelement_class;
|
||||||
|
PangoFontMap *fontmap;
|
||||||
|
|
||||||
gobject_class = (GObjectClass *) klass;
|
gobject_class = (GObjectClass *) klass;
|
||||||
gstelement_class = (GstElementClass *) 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->set_property = gst_text_render_set_property;
|
||||||
gobject_class->get_property = gst_text_render_get_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_object_class_install_property (G_OBJECT_CLASS (klass), ARG_FONT_DESC,
|
||||||
g_param_spec_string ("font-desc", "font description",
|
g_param_spec_string ("font-desc", "font description",
|
||||||
"Pango font description of font "
|
"Pango font description of font "
|
||||||
@ -217,47 +227,85 @@ gst_text_render_class_init (GstTextRenderClass * klass)
|
|||||||
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void
|
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 font_size = pango_font_description_get_size (desc) / PANGO_SCALE;
|
||||||
gint pitch = (width | 3) + 1;
|
|
||||||
gint size = pitch * height;
|
|
||||||
|
|
||||||
/* no need to keep reallocating; just keep the maximum size so far */
|
render->shadow_offset = (double) (font_size) / 13.0;
|
||||||
if (size <= render->bitmap_buffer_size) {
|
render->outline_offset = (double) (font_size) / 15.0;
|
||||||
bitmap->rows = height;
|
if (render->outline_offset < MINIMUM_OUTLINE_OFFSET)
|
||||||
bitmap->width = width;
|
render->outline_offset = MINIMUM_OUTLINE_OFFSET;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
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;
|
PangoRectangle ink_rect, logical_rect;
|
||||||
|
gint width, height;
|
||||||
|
|
||||||
pango_layout_get_pixel_extents (render->layout, &ink_rect, &logical_rect);
|
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);
|
width = logical_rect.width + render->shadow_offset;
|
||||||
render->baseline_y = ink_rect.y;
|
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
|
static void
|
||||||
@ -306,7 +354,7 @@ gst_text_render_setcaps (GstPad * pad, GstCaps * caps)
|
|||||||
|
|
||||||
GST_DEBUG ("Got caps %" GST_PTR_FORMAT, 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->width = width;
|
||||||
render->height = height;
|
render->height = height;
|
||||||
ret = TRUE;
|
ret = TRUE;
|
||||||
@ -325,92 +373,62 @@ gst_text_render_fixate_caps (GstPad * pad, GstCaps * caps)
|
|||||||
GstStructure *s = gst_caps_get_structure (caps, 0);
|
GstStructure *s = gst_caps_get_structure (caps, 0);
|
||||||
|
|
||||||
GST_DEBUG ("Fixating caps %" GST_PTR_FORMAT, caps);
|
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, "width", render->image_width);
|
||||||
gst_structure_fixate_field_nearest_int (s, "height", render->height);
|
gst_structure_fixate_field_nearest_int (s, "height", render->image_height);
|
||||||
GST_DEBUG ("Fixated to %" GST_PTR_FORMAT, caps);
|
GST_DEBUG ("Fixated to %" GST_PTR_FORMAT, caps);
|
||||||
|
|
||||||
gst_object_unref (render);
|
gst_object_unref (render);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
gst_text_renderer_bitmap_to_ayuv (GstTextRender * render, FT_Bitmap * bitmap,
|
gst_text_renderer_image_to_ayuv (GstTextRender * render, guchar * pixbuf,
|
||||||
guchar * pixbuf, gint x0, gint x1, gint y0, gint y1)
|
int xpos, int ypos, int stride)
|
||||||
{
|
{
|
||||||
int y; /* text bitmap coordinates */
|
int y; /* text bitmap coordinates */
|
||||||
int rowinc, bit_rowinc;
|
|
||||||
guchar *p, *bitp;
|
guchar *p, *bitp;
|
||||||
guchar v;
|
guchar a, r, g, b;
|
||||||
|
int width, height;
|
||||||
|
|
||||||
x0 = CLAMP (x0, 0, render->width);
|
width = render->image_width;
|
||||||
x1 = CLAMP (x1, 0, render->width);
|
height = render->image_height;
|
||||||
|
bitp = render->text_image;
|
||||||
|
|
||||||
y0 = CLAMP (y0, 0, render->height);
|
for (y = 0; y < height; y++) {
|
||||||
y1 = CLAMP (y1, 0, render->height);
|
|
||||||
|
|
||||||
|
|
||||||
rowinc = render->width - bitmap->width;
|
|
||||||
bit_rowinc = bitmap->pitch - bitmap->width;
|
|
||||||
|
|
||||||
bitp = bitmap->buffer;
|
|
||||||
p = pixbuf + ((x0 + (render->width * y0)) * 4);
|
|
||||||
|
|
||||||
for (y = y0; y < y1; y++) {
|
|
||||||
int n;
|
int n;
|
||||||
|
p = pixbuf + ypos * stride + xpos;
|
||||||
|
for (n = 0; n < width; n++) {
|
||||||
|
b = *bitp++;
|
||||||
|
g = *bitp++;
|
||||||
|
r = *bitp++;
|
||||||
|
a = *bitp++;
|
||||||
|
|
||||||
for (n = x0; n < x1; n++) {
|
*p++ = a;
|
||||||
v = *bitp;
|
*p++ = CLAMP ((int) (((19595 * r) >> 16) + ((38470 * g) >> 16) +
|
||||||
if (v) {
|
((7471 * b) >> 16)), 0, 255);
|
||||||
p[0] = v;
|
*p++ = CLAMP ((int) (-((11059 * r) >> 16) - ((21709 * g) >> 16) +
|
||||||
p[1] = 255;
|
((32768 * b) >> 16) + 128), 0, 255);
|
||||||
p[2] = 0x80;
|
*p++ = CLAMP ((int) (((32768 * r) >> 16) - ((27439 * g) >> 16) -
|
||||||
p[3] = 0x80;
|
((5329 * b) >> 16) + 128), 0, 255);
|
||||||
}
|
|
||||||
p += 4;
|
|
||||||
bitp++;
|
|
||||||
}
|
}
|
||||||
p += rowinc * 4;
|
|
||||||
bitp += bit_rowinc;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
gst_text_renderer_bitmap_to_argb (GstTextRender * render, FT_Bitmap * bitmap,
|
gst_text_renderer_image_to_argb (GstTextRender * render, guchar * pixbuf,
|
||||||
guchar * pixbuf, gint x0, gint x1, gint y0, gint y1)
|
int xpos, int ypos, int stride)
|
||||||
{
|
{
|
||||||
int y; /* text bitmap coordinates */
|
int i;
|
||||||
int rowinc, bit_rowinc;
|
|
||||||
guchar *p, *bitp;
|
guchar *p, *bitp;
|
||||||
guchar v;
|
int width, height;
|
||||||
|
|
||||||
x0 = CLAMP (x0, 0, render->width);
|
width = render->image_width;
|
||||||
x1 = CLAMP (x1, 0, render->width);
|
height = render->image_height;
|
||||||
|
bitp = render->text_image;
|
||||||
|
|
||||||
y0 = CLAMP (y0, 0, render->height);
|
for (i = 0; i < height; i++) {
|
||||||
y1 = CLAMP (y1, 0, render->height);
|
p = pixbuf + ypos * stride + xpos;
|
||||||
|
memcpy (p, bitp, width * 4);
|
||||||
|
bitp += width * 4;
|
||||||
rowinc = render->width - bitmap->width;
|
|
||||||
bit_rowinc = bitmap->pitch - bitmap->width;
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -438,7 +456,7 @@ gst_text_render_chain (GstPad * pad, GstBuffer * inbuf)
|
|||||||
/* render text */
|
/* render text */
|
||||||
GST_DEBUG ("rendering '%*s'", size, data);
|
GST_DEBUG ("rendering '%*s'", size, data);
|
||||||
pango_layout_set_markup (render->layout, (gchar *) data, size);
|
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);
|
gst_text_render_check_argb (render);
|
||||||
|
|
||||||
@ -487,10 +505,10 @@ gst_text_render_chain (GstPad * pad, GstBuffer * inbuf)
|
|||||||
xpos = render->xpad;
|
xpos = render->xpad;
|
||||||
break;
|
break;
|
||||||
case GST_TEXT_RENDER_HALIGN_CENTER:
|
case GST_TEXT_RENDER_HALIGN_CENTER:
|
||||||
xpos = (render->width - render->bitmap.width) / 2;
|
xpos = (render->width - render->image_width) / 2;
|
||||||
break;
|
break;
|
||||||
case GST_TEXT_RENDER_HALIGN_RIGHT:
|
case GST_TEXT_RENDER_HALIGN_RIGHT:
|
||||||
xpos = render->width - render->bitmap.width - render->xpad;
|
xpos = render->width - render->image_width - render->xpad;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
xpos = 0;
|
xpos = 0;
|
||||||
@ -498,10 +516,10 @@ gst_text_render_chain (GstPad * pad, GstBuffer * inbuf)
|
|||||||
|
|
||||||
switch (render->valign) {
|
switch (render->valign) {
|
||||||
case GST_TEXT_RENDER_VALIGN_BOTTOM:
|
case GST_TEXT_RENDER_VALIGN_BOTTOM:
|
||||||
ypos = render->height - render->bitmap.rows - render->ypad;
|
ypos = render->height - render->image_height - render->ypad;
|
||||||
break;
|
break;
|
||||||
case GST_TEXT_RENDER_VALIGN_BASELINE:
|
case GST_TEXT_RENDER_VALIGN_BASELINE:
|
||||||
ypos = render->height - (render->bitmap.rows + render->ypad);
|
ypos = render->height - (render->image_height + render->ypad);
|
||||||
break;
|
break;
|
||||||
case GST_TEXT_RENDER_VALIGN_TOP:
|
case GST_TEXT_RENDER_VALIGN_TOP:
|
||||||
ypos = render->ypad;
|
ypos = render->ypad;
|
||||||
@ -511,13 +529,13 @@ gst_text_render_chain (GstPad * pad, GstBuffer * inbuf)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (render->bitmap.buffer) {
|
if (render->text_image) {
|
||||||
if (render->use_ARGB) {
|
if (render->use_ARGB) {
|
||||||
gst_text_renderer_bitmap_to_argb (render, &render->bitmap, data, xpos,
|
gst_text_renderer_image_to_argb (render, data, xpos, ypos,
|
||||||
xpos + render->bitmap.width, ypos, ypos + render->bitmap.rows);
|
render->width * 4);
|
||||||
} else {
|
} else {
|
||||||
gst_text_renderer_bitmap_to_ayuv (render, &render->bitmap, data, xpos,
|
gst_text_renderer_image_to_ayuv (render, data, xpos, ypos,
|
||||||
xpos + render->bitmap.width, ypos, ypos + render->bitmap.rows);
|
render->width * 4);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -536,7 +554,7 @@ gst_text_render_finalize (GObject * object)
|
|||||||
{
|
{
|
||||||
GstTextRender *render = GST_TEXT_RENDER (object);
|
GstTextRender *render = GST_TEXT_RENDER (object);
|
||||||
|
|
||||||
g_free (render->bitmap.buffer);
|
g_free (render->text_image);
|
||||||
|
|
||||||
if (render->layout)
|
if (render->layout)
|
||||||
g_object_unref (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_new (GST_TEXT_RENDER_GET_CLASS (render)->pango_context);
|
||||||
pango_layout_set_alignment (render->layout,
|
pango_layout_set_alignment (render->layout,
|
||||||
(PangoAlignment) render->line_align);
|
(PangoAlignment) render->line_align);
|
||||||
memset (&render->bitmap, 0, sizeof (render->bitmap));
|
|
||||||
|
|
||||||
render->halign = DEFAULT_PROP_HALIGNMENT;
|
render->halign = DEFAULT_PROP_HALIGNMENT;
|
||||||
render->valign = DEFAULT_PROP_VALIGNMENT;
|
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_LOG ("font description set: %s", g_value_get_string (value));
|
||||||
GST_OBJECT_LOCK (render);
|
GST_OBJECT_LOCK (render);
|
||||||
pango_layout_set_font_description (render->layout, desc);
|
pango_layout_set_font_description (render->layout, desc);
|
||||||
|
gst_text_render_adjust_values_with_fontdesc (render, desc);
|
||||||
pango_font_description_free (desc);
|
pango_font_description_free (desc);
|
||||||
gst_text_render_render_text (render);
|
gst_text_render_render_pangocairo (render);
|
||||||
GST_OBJECT_UNLOCK (render);
|
GST_OBJECT_UNLOCK (render);
|
||||||
} else {
|
} else {
|
||||||
GST_WARNING ("font description parse failed: %s",
|
GST_WARNING ("font description parse failed: %s",
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
#define __GST_TEXT_RENDER_H__
|
#define __GST_TEXT_RENDER_H__
|
||||||
|
|
||||||
#include <gst/gst.h>
|
#include <gst/gst.h>
|
||||||
#include <pango/pangoft2.h>
|
#include <pango/pangocairo.h>
|
||||||
|
|
||||||
G_BEGIN_DECLS
|
G_BEGIN_DECLS
|
||||||
|
|
||||||
@ -75,8 +75,11 @@ struct _GstTextRender {
|
|||||||
gint width;
|
gint width;
|
||||||
gint height;
|
gint height;
|
||||||
PangoLayout *layout;
|
PangoLayout *layout;
|
||||||
FT_Bitmap bitmap;
|
gdouble shadow_offset;
|
||||||
gint bitmap_buffer_size;
|
gdouble outline_offset;
|
||||||
|
guchar *text_image;
|
||||||
|
gint image_width;
|
||||||
|
gint image_height;
|
||||||
gint baseline_y;
|
gint baseline_y;
|
||||||
gboolean use_ARGB;
|
gboolean use_ARGB;
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user