From: Young-Ho Cha Date: Tue, 4 Aug 2009 09:35:10 +0000 (+0200) Subject: pango: Use pango-cairo instead of pango-ft2 X-Git-Tag: 1.19.3~511^2~9376 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=7608c31516464783fe4b2194775960cfe2bc2cd0;p=platform%2Fupstream%2Fgstreamer.git 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. --- diff --git a/ext/pango/gsttextoverlay.c b/ext/pango/gsttextoverlay.c index 168e911..5eeecd5 100644 --- a/ext/pango/gsttextoverlay.c +++ b/ext/pango/gsttextoverlay.c @@ -4,6 +4,7 @@ * Copyright (C) <2006> Julien Moutte * Copyright (C) <2006> Zeeshan Ali * Copyright (C) <2006-2008> Tim-Philipp Müller + * Copyright (C) <2009> Young-Ho Cha * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -80,6 +81,7 @@ #include "gsttimeoverlay.h" #include "gstclockoverlay.h" #include "gsttextrender.h" +#include /* FIXME: * - use proper strides and offset for I420 @@ -113,10 +115,38 @@ GST_ELEMENT_DETAILS ("Text overlay", #define DEFAULT_PROP_SILENT FALSE #define DEFAULT_PROP_LINE_ALIGNMENT GST_TEXT_OVERLAY_LINE_ALIGN_CENTER #define DEFAULT_PROP_WAIT_TEXT TRUE +#define DEFAULT_PROP_AUTO_ADJUST_SIZE TRUE +#define DEFAULT_PROP_VERTICAL_RENDER FALSE /* make a property of me */ #define DEFAULT_SHADING_VALUE -80 +#define MINIMUM_OUTLINE_OFFSET 1.0 +#define DEFAULT_SCALE_BASIS 640 + +#define COMP_Y(ret, r, g, b) \ +{ \ + ret = (int) (((19595 * r) >> 16) + ((38470 * g) >> 16) + ((7471 * b) >> 16)); \ + ret = CLAMP (ret, 0, 255); \ +} + +#define COMP_U(ret, r, g, b) \ +{ \ + ret = (int) (-((11059 * r) >> 16) - ((21709 * g) >> 16) + ((32768 * b) >> 16) + 128); \ + ret = CLAMP (ret, 0, 255); \ +} + +#define COMP_V(ret, r, g, b) \ +{ \ + ret = (int) (((32768 * r) >> 16) - ((27439 * g) >> 16) - ((5329 * b) >> 16) + 128); \ + ret = CLAMP (ret, 0, 255); \ +} + +#define BLEND(ret, alpha, v0, v1) \ +{ \ + ret = (v0 * alpha + v1 * (255 - alpha)) / 255; \ +} + enum { PROP_0, @@ -135,6 +165,8 @@ enum PROP_SILENT, PROP_LINE_ALIGNMENT, PROP_WAIT_TEXT, + PROP_AUTO_ADJUST_SIZE, + PROP_VERTICAL_RENDER, PROP_LAST }; @@ -265,12 +297,15 @@ static GstPadLinkReturn gst_text_overlay_text_pad_link (GstPad * pad, GstPad * peer); static void gst_text_overlay_text_pad_unlink (GstPad * pad); static void gst_text_overlay_pop_text (GstTextOverlay * overlay); +static void gst_text_overlay_update_render_mode (GstTextOverlay * overlay); static void gst_text_overlay_finalize (GObject * object); static void gst_text_overlay_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec); static void gst_text_overlay_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec); +static void gst_text_overlay_adjust_values_with_fontdesc (GstTextOverlay * + overlay, PangoFontDescription * desc); GST_BOILERPLATE (GstTextOverlay, gst_text_overlay, GstElement, GST_TYPE_ELEMENT) @@ -304,6 +339,7 @@ gst_text_overlay_class_init (GstTextOverlayClass * klass) { GObjectClass *gobject_class; GstElementClass *gstelement_class; + PangoFontMap *fontmap; gobject_class = (GObjectClass *) klass; gstelement_class = (GstElementClass *) klass; @@ -316,7 +352,9 @@ gst_text_overlay_class_init (GstTextOverlayClass * klass) GST_DEBUG_FUNCPTR (gst_text_overlay_change_state); klass->get_text = gst_text_overlay_get_text; - klass->pango_context = pango_ft2_get_context (72, 72); + fontmap = pango_cairo_font_map_get_default (); + klass->pango_context = + pango_cairo_font_map_create_context (PANGO_CAIRO_FONT_MAP (fontmap)); g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_TEXT, g_param_spec_string ("text", "text", @@ -408,6 +446,16 @@ gst_text_overlay_class_init (GstTextOverlayClass * klass) g_param_spec_boolean ("wait-text", "Wait Text", "Whether to wait for subtitles", DEFAULT_PROP_WAIT_TEXT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (G_OBJECT_CLASS (klass), + PROP_AUTO_ADJUST_SIZE, g_param_spec_boolean ("auto-resize", "auto resize", + "Automatically adjust font size to screen-size.", + DEFAULT_PROP_AUTO_ADJUST_SIZE, G_PARAM_READWRITE)); + g_object_class_install_property (G_OBJECT_CLASS (klass), + PROP_VERTICAL_RENDER, g_param_spec_boolean ("vertical-render", + "vertical render", "Vertical Render.", DEFAULT_PROP_VERTICAL_RENDER, + G_PARAM_READWRITE)); + } static void @@ -416,7 +464,11 @@ gst_text_overlay_finalize (GObject * object) GstTextOverlay *overlay = GST_TEXT_OVERLAY (object); g_free (overlay->default_text); - g_free (overlay->bitmap.buffer); + + if (overlay->text_image) { + g_free (overlay->text_image); + overlay->text_image = NULL; + } if (overlay->layout) { g_object_unref (overlay->layout); @@ -445,6 +497,7 @@ static void gst_text_overlay_init (GstTextOverlay * overlay, GstTextOverlayClass * klass) { GstPadTemplate *template; + PangoFontDescription *desc; /* video sink */ template = gst_static_pad_template_get (&video_sink_template_factory); @@ -491,9 +544,10 @@ gst_text_overlay_init (GstTextOverlay * overlay, GstTextOverlayClass * klass) overlay->line_align = DEFAULT_PROP_LINE_ALIGNMENT; overlay->layout = pango_layout_new (GST_TEXT_OVERLAY_GET_CLASS (overlay)->pango_context); - pango_layout_set_alignment (overlay->layout, - (PangoAlignment) overlay->line_align); - memset (&overlay->bitmap, 0, sizeof (overlay->bitmap)); + desc = + pango_context_get_font_description (GST_TEXT_OVERLAY_GET_CLASS + (overlay)->pango_context); + gst_text_overlay_adjust_values_with_fontdesc (overlay, desc); overlay->halign = DEFAULT_PROP_HALIGNMENT; overlay->valign = DEFAULT_PROP_VALIGNMENT; @@ -508,9 +562,13 @@ gst_text_overlay_init (GstTextOverlay * overlay, GstTextOverlayClass * klass) overlay->shading_value = DEFAULT_SHADING_VALUE; overlay->silent = DEFAULT_PROP_SILENT; overlay->wait_text = DEFAULT_PROP_WAIT_TEXT; + overlay->auto_adjust_size = DEFAULT_PROP_AUTO_ADJUST_SIZE; overlay->default_text = g_strdup (DEFAULT_PROP_TEXT); overlay->need_render = TRUE; + overlay->text_image = NULL; + overlay->use_vertical_render = DEFAULT_PROP_VERTICAL_RENDER; + gst_text_overlay_update_render_mode (overlay); overlay->fps_n = 0; overlay->fps_d = 1; @@ -534,13 +592,48 @@ gst_text_overlay_update_wrap_mode (GstTextOverlay * overlay) GST_DEBUG_OBJECT (overlay, "Set wrap mode NONE"); pango_layout_set_width (overlay->layout, -1); } else { + int width; + + if (overlay->auto_adjust_size) { + width = DEFAULT_SCALE_BASIS * PANGO_SCALE; + if (overlay->use_vertical_render) { + width = width * (overlay->height - overlay->ypad * 2) / overlay->width; + } + } else { + width = + (overlay->use_vertical_render ? overlay->height : overlay->width) * + PANGO_SCALE; + } + GST_DEBUG_OBJECT (overlay, "Set layout width %d", overlay->width); GST_DEBUG_OBJECT (overlay, "Set wrap mode %d", overlay->wrap_mode); - pango_layout_set_width (overlay->layout, overlay->width * PANGO_SCALE); + pango_layout_set_width (overlay->layout, width); pango_layout_set_wrap (overlay->layout, (PangoWrapMode) overlay->wrap_mode); } } +static void +gst_text_overlay_update_render_mode (GstTextOverlay * overlay) +{ +#if HAVE_PANGO_VERTICAL_WRITING + PangoMatrix matrix = PANGO_MATRIX_INIT; + PangoContext *context = pango_layout_get_context (overlay->layout); + + if (overlay->use_vertical_render) { + pango_matrix_rotate (&matrix, -90); + pango_context_set_base_gravity (context, PANGO_GRAVITY_AUTO); + pango_context_set_matrix (context, &matrix); + pango_layout_set_alignment (overlay->layout, PANGO_ALIGN_LEFT); + } else { + pango_context_set_base_gravity (context, PANGO_GRAVITY_SOUTH); + pango_context_set_matrix (context, &matrix); +#endif + pango_layout_set_alignment (overlay->layout, overlay->line_align); +#if HAVE_PANGO_VERTICAL_WRITING + } +#endif +} + static gboolean gst_text_overlay_setcaps_txt (GstPad * pad, GstCaps * caps) { @@ -677,6 +770,7 @@ gst_text_overlay_set_property (GObject * object, guint prop_id, if (desc) { GST_LOG_OBJECT (overlay, "font description set: %s", fontdesc_str); pango_layout_set_font_description (overlay->layout, desc); + gst_text_overlay_adjust_values_with_fontdesc (overlay, desc); pango_font_description_free (desc); } else { GST_WARNING_OBJECT (overlay, "font description parse failed: %s", @@ -695,6 +789,19 @@ gst_text_overlay_set_property (GObject * object, guint prop_id, case PROP_WAIT_TEXT: overlay->wait_text = g_value_get_boolean (value); break; + case PROP_AUTO_ADJUST_SIZE: + { + overlay->auto_adjust_size = g_value_get_boolean (value); + overlay->need_render = TRUE; + } +#ifdef HAVE_PANGO_VERTICAL_WRITING + case PROP_VERTICAL_RENDER: + { + overlay->use_vertical_render = g_value_get_boolean (value); + gst_text_overlay_update_render_mode (overlay); + overlay->need_render = TRUE; + } +#endif default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -748,6 +855,16 @@ gst_text_overlay_get_property (GObject * object, guint prop_id, case PROP_WAIT_TEXT: g_value_set_boolean (value, overlay->wait_text); break; + case PROP_AUTO_ADJUST_SIZE: + g_value_set_boolean (value, overlay->auto_adjust_size); + break; + case PROP_VERTICAL_RENDER: +#ifdef HAVE_PANGO_VERTICAL_WRITING + g_value_set_boolean (value, overlay->use_vertical_render); +#else + g_value_set_boolean (value, FALSE); +#endif + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -854,6 +971,285 @@ gst_text_overlay_getcaps (GstPad * pad) return caps; } +static void +gst_text_overlay_adjust_values_with_fontdesc (GstTextOverlay * overlay, + PangoFontDescription * desc) +{ + gint font_size = pango_font_description_get_size (desc) / PANGO_SCALE; + overlay->shadow_offset = (double) (font_size) / 13.0; + overlay->outline_offset = (double) (font_size) / 15.0; + if (overlay->outline_offset < MINIMUM_OUTLINE_OFFSET) + overlay->outline_offset = MINIMUM_OUTLINE_OFFSET; +} + +static inline void +gst_text_overlay_blit_1 (GstTextOverlay * overlay, guchar * dest, gint xpos, + gint ypos, guchar * text_image, guint dest_stride) +{ + gint i, j = 0; + gint x, y; + guchar r, g, b, a; + guchar *pimage; + guchar *py; + gint width = overlay->image_width; + gint height = overlay->image_height; + + if (xpos < 0) { + xpos = 0; + } + + if (xpos + width > overlay->width) { + width = overlay->width - xpos; + } + + if (ypos + height > overlay->height) { + height = overlay->height - ypos; + } + + dest += (ypos / 1) * dest_stride; + + for (i = 0; i < height; i++) { + pimage = text_image + 4 * (i * overlay->image_width); + py = dest + i * dest_stride + xpos; + for (j = 0; j < width; j++) { + b = *pimage++; + g = *pimage++; + r = *pimage++; + a = *pimage++; + if (a == 0) { + py++; + continue; + } + COMP_Y (y, r, g, b); + x = *py; + BLEND (*py++, a, y, x); + } + } +} + +static inline void +gst_text_overlay_blit_sub2x2cbcr (GstTextOverlay * overlay, + guchar * destcb, guchar * destcr, gint xpos, gint ypos, guchar * text_image, + guint destcb_stride, guint destcr_stride) +{ + gint i, j; + gint x, cb, cr; + gushort r, g, b, a; + guchar *pimage1, *pimage2; + guchar *pcb, *pcr; + gint width = overlay->image_width - 2; + gint height = overlay->image_height - 2; + + if (xpos < 0) { + xpos = 0; + } + + if (xpos + width > overlay->width) { + width = overlay->width - xpos; + } + + if (ypos + height > overlay->height) { + height = overlay->height - ypos; + } + + destcb += (ypos / 2) * destcb_stride; + destcr += (ypos / 2) * destcr_stride; + + for (i = 0; i < height; i += 2) { + pimage1 = text_image + 4 * (i * overlay->image_width); + pimage2 = pimage1 + 4 * overlay->image_width; + pcb = destcb + (i / 2) * destcb_stride + xpos / 2; + pcr = destcr + (i / 2) * destcr_stride + xpos / 2; + for (j = 0; j < width; j += 2) { + b = *pimage1++; + g = *pimage1++; + r = *pimage1++; + a = *pimage1++; + + b += *pimage1++; + g += *pimage1++; + r += *pimage1++; + a += *pimage1++; + + b += *pimage2++; + g += *pimage2++; + r += *pimage2++; + a += *pimage2++; + + /* for rounding */ + b += *pimage2++ + 2; + g += *pimage2++ + 2; + r += *pimage2++ + 2; + a += *pimage2++ + 2; + + b /= 4; + g /= 4; + r /= 4; + a /= 4; + + if (a == 0) { + pcb++; + pcr++; + continue; + } + COMP_U (cb, r, g, b); + COMP_V (cr, r, g, b) + + x = *pcb; + BLEND (*pcb++, a, cb, x); + x = *pcr; + BLEND (*pcr++, a, cr, x); + } + } +} + +static void +gst_text_overlay_render_pangocairo (GstTextOverlay * overlay, + const gchar * string, gint textlen) +{ + cairo_t *cr; + cairo_surface_t *surface; + cairo_t *cr_shadow; + cairo_surface_t *surface_shadow; + PangoRectangle ink_rect, logical_rect; + cairo_matrix_t cairo_matrix; + int width, height; + double scalef = 1.0; + + if (overlay->auto_adjust_size) { + /* 640 pixel is default */ + scalef = (double) (overlay->width) / DEFAULT_SCALE_BASIS; + } + pango_layout_set_width (overlay->layout, -1); + /* set text on pango layout */ + pango_layout_set_markup (overlay->layout, string, textlen); + + /* get subtitle image size */ + pango_layout_get_pixel_extents (overlay->layout, &ink_rect, &logical_rect); + + width = (logical_rect.width + overlay->shadow_offset) * scalef; + + if (width + overlay->deltax > + (overlay->use_vertical_render ? overlay->height : overlay->width)) { + /* + * subtitle image width is larger then overlay width + * so rearrange overlay wrap mode. + */ + gst_text_overlay_update_wrap_mode (overlay); + pango_layout_get_pixel_extents (overlay->layout, &ink_rect, &logical_rect); + width = overlay->width; + } + + height = + (logical_rect.height + logical_rect.y + overlay->shadow_offset) * scalef; + if (height > overlay->height) { + height = overlay->height; + } +#ifdef HAVE_PANGO_VERTICAL_WRITING + if (overlay->use_vertical_render) { + PangoRectangle rect; + PangoContext *context; + PangoMatrix matrix = PANGO_MATRIX_INIT; + int tmp; + + context = pango_layout_get_context (overlay->layout); + + pango_matrix_rotate (&matrix, -90); + + rect.x = rect.y = 0; + rect.width = width; + rect.height = height; + pango_matrix_transform_pixel_rectangle (&matrix, &rect); + matrix.x0 = -rect.x; + matrix.y0 = -rect.y; + + pango_context_set_matrix (context, &matrix); + + cairo_matrix.xx = matrix.xx; + cairo_matrix.yx = matrix.yx; + cairo_matrix.xy = matrix.xy; + cairo_matrix.yy = matrix.yy; + cairo_matrix.x0 = matrix.x0; + cairo_matrix.y0 = matrix.y0; + cairo_matrix_scale (&cairo_matrix, scalef, scalef); + + tmp = height; + height = width; + width = tmp; + } else +#endif + { + cairo_matrix_init_scale (&cairo_matrix, scalef, scalef); + } + /* clear shadow surface */ + surface_shadow = cairo_image_surface_create (CAIRO_FORMAT_A8, width, height); + cr_shadow = cairo_create (surface_shadow); + + cairo_set_operator (cr_shadow, CAIRO_OPERATOR_CLEAR); + + cairo_paint (cr_shadow); + cairo_set_operator (cr_shadow, CAIRO_OPERATOR_OVER); + + cairo_save (cr_shadow); + cairo_set_matrix (cr_shadow, &cairo_matrix); + + cairo_save (cr_shadow); + /* draw shadow text */ + cairo_set_source_rgba (cr_shadow, 0.0, 0.0, 0.0, 0.5); + cairo_translate (cr_shadow, overlay->shadow_offset, overlay->shadow_offset); + pango_cairo_show_layout (cr_shadow, overlay->layout); + cairo_restore (cr_shadow); + + /* draw outline text */ + cairo_save (cr_shadow); + cairo_set_source_rgb (cr_shadow, 0.0, 0.0, 0.0); + cairo_set_line_width (cr_shadow, overlay->outline_offset); + pango_cairo_layout_path (cr_shadow, overlay->layout); + cairo_stroke (cr_shadow); + cairo_restore (cr_shadow); + + if (overlay->want_shading) { + cairo_paint_with_alpha (cr_shadow, overlay->shading_value); + } + cairo_restore (cr_shadow); + cairo_destroy (cr_shadow); + + /* clear image surface */ + overlay->text_image = g_realloc (overlay->text_image, 4 * width * height); + + surface = cairo_image_surface_create_for_data (overlay->text_image, + CAIRO_FORMAT_ARGB32, width, height, width * 4); + cr = cairo_create (surface); + cairo_set_operator (cr, CAIRO_OPERATOR_CLEAR); + cairo_paint (cr); + cairo_set_operator (cr, CAIRO_OPERATOR_OVER); + + /* set default color */ + cairo_set_source_rgb (cr, 1.0, 1.0, 1.0); + + cairo_save (cr); + cairo_set_matrix (cr, &cairo_matrix); + /* draw text */ + cairo_set_matrix (cr, &cairo_matrix); + /* draw text */ + pango_cairo_show_layout (cr, overlay->layout); + cairo_restore (cr); + + /* composite outline, shadow, and text */ + cairo_set_operator (cr, CAIRO_OPERATOR_DEST_OVER); + cairo_set_source_surface (cr, surface_shadow, 0.0, 0.0); + cairo_paint (cr); + + cairo_destroy (cr); + cairo_surface_destroy (surface_shadow); + cairo_surface_destroy (surface); + overlay->image_width = width; + overlay->image_height = height; + overlay->baseline_y = ink_rect.y; +} + + + #define BOX_XPAD 6 #define BOX_YPAD 6 @@ -915,21 +1311,12 @@ gst_text_overlay_shade_UYVY_y (GstTextOverlay * overlay, guchar * dest, */ static inline void -gst_text_overlay_blit_I420 (GstTextOverlay * overlay, FT_Bitmap * bitmap, - guint8 * yuv_pixels, gint x0, gint y0) +gst_text_overlay_blit_I420 (GstTextOverlay * overlay, + guint8 * yuv_pixels, gint xpos, gint ypos) { - int y; /* text bitmap coordinates */ - int x1; /* video buffer coordinates */ - guint8 *y_p, *bitp, *u_p, *v_p; - int bitmap_x0 = 0; //x0 < 1 ? -(x0 - 1) : 1; /* 1 pixel border */ - int bitmap_y0 = y0 < 1 ? -(y0 - 1) : 1; /* 1 pixel border */ - int bitmap_width = bitmap->width - bitmap_x0; - int bitmap_height = bitmap->rows - bitmap_y0; - int skip_y, skip_x; int y_stride, u_stride, v_stride; int u_offset, v_offset; int h, w; - guint8 v; w = overlay->width; h = overlay->height; @@ -942,177 +1329,90 @@ gst_text_overlay_blit_I420 (GstTextOverlay * overlay, FT_Bitmap * bitmap, v_offset = gst_video_format_get_component_offset (GST_VIDEO_FORMAT_I420, 2, w, h); -/* - if (x0 < 0 && abs (x0) < bitmap_width) { - bitmap_x0 = abs (x0); - x0 = 0; - } -*/ - - if (x0 + bitmap_x0 + bitmap_width > w - 1) /* 1 pixel border */ - bitmap_width -= x0 + bitmap_x0 + bitmap_width - w + 1; - if (y0 + bitmap_y0 + bitmap_height > h - 1) /* 1 pixel border */ - bitmap_height -= y0 + bitmap_y0 + bitmap_height - h + 1; - - x1 = x0 + bitmap_x0; - - /* draw an outline around the text */ - for (y = bitmap_y0; y < bitmap_y0 + bitmap_height; y++) { - int n; - - bitp = bitmap->buffer + (y * bitmap->pitch) + bitmap_x0; - y_p = yuv_pixels + ((y + y0) * y_stride) + x1; - for (n = bitmap_width; n > 0; --n) { - v = *bitp; - if (v) { - y_p[-1] = CLAMP (y_p[-1] - v, 0, 255); - y_p[1] = CLAMP (y_p[1] - v, 0, 255); - y_p[-w] = CLAMP (y_p[-w] - v, 0, 255); - y_p[w] = CLAMP (y_p[w] - v, 0, 255); - } - y_p++; - bitp++; - } - } - - /* now blit text */ - x1 = x0 + bitmap_x0; - skip_y = 0; - for (y = bitmap_y0; y < bitmap_y0 + bitmap_height; y++) { - int n; - - bitp = bitmap->buffer + (y * bitmap->pitch) + bitmap_x0; - - y_p = yuv_pixels + 0 + ((y0 + y) * y_stride) + x1; - u_p = yuv_pixels + u_offset + (((y0 + y) / 2) * u_stride) + (x1 / 2); - v_p = yuv_pixels + v_offset + (((y0 + y) / 2) * v_stride) + (x1 / 2); - - skip_x = 0; - for (n = bitmap_width; n > 0; --n) { - v = *bitp; - if (v) { - *y_p = v; - if (!skip_y) { - *u_p = 0x80; - *v_p = 0x80; - } - } - if (!skip_y) { - if (!skip_x) { - u_p++; - v_p++; - } - skip_x = !skip_x; - } - y_p++; - bitp++; - } - skip_y = !skip_y; - } + gst_text_overlay_blit_1 (overlay, yuv_pixels, xpos, ypos, overlay->text_image, + y_stride); + gst_text_overlay_blit_sub2x2cbcr (overlay, yuv_pixels + u_offset, + yuv_pixels + v_offset, xpos, ypos, overlay->text_image, u_stride, + v_stride); } static inline void -gst_text_overlay_blit_UYVY (GstTextOverlay * overlay, FT_Bitmap * bitmap, - guint8 * yuv_pixels, gint x0, gint y0) +gst_text_overlay_blit_UYVY (GstTextOverlay * overlay, + guint8 * yuv_pixels, gint xpos, gint ypos) { - int y; /* text bitmap coordinates */ - int x1, y1; /* video buffer coordinates */ - guint8 *p, *bitp; - int video_width, video_height; - int bitmap_x0 = 0; //x0 < 1 ? -(x0 - 1) : 1; /* 1 pixel border */ - int bitmap_y0 = y0 < 1 ? -(y0 - 1) : 1; /* 1 pixel border */ - int bitmap_width = bitmap->width - bitmap_x0; - int bitmap_height = bitmap->rows - bitmap_y0; - - video_width = - gst_video_format_get_row_stride (GST_VIDEO_FORMAT_UYVY, 0, - overlay->width); - video_height = overlay->height; - - /*g_debug ("bliting bitmap(%d, %d) on yuv (%d,%d) at %d,%d", - bitmap_width, bitmap_height, - video_width, video_height, - x0, y0); */ - - if (x0 + bitmap_x0 + bitmap_width > overlay->width - 1) /* 1 pixel border */ - bitmap_width -= x0 + bitmap_x0 + bitmap_width - overlay->width + 1; - if (y0 + bitmap_y0 + bitmap_height > video_height - 1) /* 1 pixel border */ - bitmap_height -= y0 + bitmap_y0 + bitmap_height - video_height + 1; - - x1 = x0 + bitmap_x0; - y1 = y0 + bitmap_y0; - - /* draw an outline around the text */ - for (y = bitmap_y0; y < bitmap_y0 + bitmap_height; y++) { - int n; - - bitp = bitmap->buffer + (y * bitmap->pitch) + bitmap_x0; - p = yuv_pixels + (y0 + y) * video_width + (x1 * 2) + 1; - for (n = bitmap_width; n > 0; --n) { - if (*bitp) { - *(p + 2) = CLAMP (*(p + 2) - *bitp, 0, 255); - *(p - 2) = CLAMP (*(p - 2) - *bitp, 0, 255); - *(p - video_width) = CLAMP (*(p - video_width) - *bitp, 0, 255); - *(p + video_width) = CLAMP (*(p + video_width) - *bitp, 0, 255); - } - p += 2; - bitp++; - } + int a0, r0, g0, b0; + int a1, r1, g1, b1; + int y0, y1, u, v; + int i, j; + int h, w; + guchar *pimage, *dest; + + w = overlay->image_width - 2; + h = overlay->image_height - 2; + + if (xpos < 0) { + xpos = 0; } - /* now blit text */ - for (y = bitmap_y0; y < bitmap_y0 + bitmap_height; y++) { - int n; + if (xpos + w > overlay->width) { + w = overlay->width - xpos; + } - bitp = bitmap->buffer + (y * bitmap->pitch) + bitmap_x0; - p = yuv_pixels + (y0 + y) * video_width + (x1 * 2) + 1; + if (ypos + h > overlay->height) { + h = overlay->height - ypos; + } - for (n = bitmap_width; n > 0; --n) { - if (*bitp) { - *p = *bitp; - *(p - 1) = 0x80; + for (i = 0; i < h; i++) { + pimage = overlay->text_image + i * overlay->image_width * 4; + dest = yuv_pixels + (i + ypos) * overlay->width * 2 + xpos * 2; + for (j = 0; j < w; j += 2) { + b0 = *pimage++; + g0 = *pimage++; + r0 = *pimage++; + a0 = *pimage++; + + b1 = *pimage++; + g1 = *pimage++; + r1 = *pimage++; + a1 = *pimage++; + + a0 += a1 + 2; + a0 /= 2; + if (a0 == 0) { + dest += 4; + continue; } - p += 2; - bitp++; - } - } -} + COMP_Y (y0, r0, g0, b0); + COMP_Y (y1, r1, g1, b1); -static void -gst_text_overlay_resize_bitmap (GstTextOverlay * overlay, gint width, - gint height) -{ - FT_Bitmap *bitmap = &overlay->bitmap; - int pitch = (width | 3) + 1; - int size = pitch * height; - - /* no need to keep reallocating; just keep the maximum size so far */ - if (size <= overlay->bitmap_buffer_size) { - bitmap->rows = height; - bitmap->width = width; - bitmap->pitch = pitch; - memset (bitmap->buffer, 0, overlay->bitmap_buffer_size); - return; - } - if (!bitmap->buffer) { - /* initialize */ - bitmap->pixel_mode = ft_pixel_mode_grays; - bitmap->num_grays = 256; + b0 += b1 + 2; + g0 += g1 + 2; + r0 += r1 + 2; + + b0 /= 2; + g0 /= 2; + r0 /= 2; + + COMP_U (u, r0, g0, b0); + COMP_V (v, r0, g0, b0); + + BLEND (*dest, a0, u, *dest); + dest++; + BLEND (*dest, a0, y0, *dest); + dest++; + BLEND (*dest, a0, v, *dest); + dest++; + BLEND (*dest, a0, y1, *dest); + dest++; + } } - overlay->bitmap_buffer_size = size; - bitmap->buffer = g_realloc (bitmap->buffer, size); - memset (bitmap->buffer, 0, size); - bitmap->rows = height; - bitmap->width = width; - bitmap->pitch = pitch; } static void gst_text_overlay_render_text (GstTextOverlay * overlay, const gchar * text, gint textlen) { - PangoRectangle ink_rect, logical_rect; gchar *string; if (!overlay->need_render) { @@ -1136,13 +1436,7 @@ gst_text_overlay_render_text (GstTextOverlay * overlay, /* FIXME: should we check for UTF-8 here? */ GST_DEBUG ("Rendering '%s'", string); - pango_layout_set_markup (overlay->layout, string, textlen); - - pango_layout_get_pixel_extents (overlay->layout, &ink_rect, &logical_rect); - gst_text_overlay_resize_bitmap (overlay, ink_rect.width, - ink_rect.height + ink_rect.y); - pango_ft2_render_layout (&overlay->bitmap, overlay->layout, -ink_rect.x, 0); - overlay->baseline_y = ink_rect.y; + gst_text_overlay_render_pangocairo (overlay, string, textlen); g_free (string); @@ -1153,31 +1447,45 @@ static GstFlowReturn gst_text_overlay_push_frame (GstTextOverlay * overlay, GstBuffer * video_frame) { gint xpos, ypos; + gint width, height; + GstTextOverlayVAlign valign; + GstTextOverlayHAlign halign; + + width = overlay->image_width; + height = overlay->image_height; - video_frame = gst_buffer_make_writable (video_frame); - switch (overlay->halign) { + if (overlay->use_vertical_render) + halign = GST_TEXT_OVERLAY_HALIGN_RIGHT; + else + halign = overlay->halign; + + switch (halign) { case GST_TEXT_OVERLAY_HALIGN_LEFT: xpos = overlay->xpad; break; case GST_TEXT_OVERLAY_HALIGN_CENTER: - xpos = (overlay->width - overlay->bitmap.width) / 2; + xpos = (overlay->width - width) / 2; break; case GST_TEXT_OVERLAY_HALIGN_RIGHT: - xpos = overlay->width - overlay->bitmap.width - overlay->xpad; + xpos = overlay->width - width - overlay->xpad; break; default: xpos = 0; } xpos += overlay->deltax; + if (overlay->use_vertical_render) + valign = GST_TEXT_OVERLAY_VALIGN_TOP; + else + valign = overlay->valign; - switch (overlay->valign) { + switch (valign) { case GST_TEXT_OVERLAY_VALIGN_BOTTOM: - ypos = overlay->height - overlay->bitmap.rows - overlay->ypad; + ypos = overlay->height - height - overlay->ypad; break; case GST_TEXT_OVERLAY_VALIGN_BASELINE: - ypos = overlay->height - (overlay->bitmap.rows + overlay->ypad); + ypos = overlay->height - (height + overlay->ypad); break; case GST_TEXT_OVERLAY_VALIGN_TOP: ypos = overlay->ypad; @@ -1188,40 +1496,23 @@ gst_text_overlay_push_frame (GstTextOverlay * overlay, GstBuffer * video_frame) } ypos += overlay->deltay; - /* shaded background box */ - if (overlay->want_shading) { - switch (overlay->format) { - case GST_MAKE_FOURCC ('I', '4', '2', '0'): - gst_text_overlay_shade_I420_y (overlay, - GST_BUFFER_DATA (video_frame), xpos, xpos + overlay->bitmap.width, - ypos, ypos + overlay->bitmap.rows); - break; - case GST_MAKE_FOURCC ('U', 'Y', 'V', 'Y'): - gst_text_overlay_shade_UYVY_y (overlay, - GST_BUFFER_DATA (video_frame), xpos, xpos + overlay->bitmap.width, - ypos, ypos + overlay->bitmap.rows); - break; - default: - g_assert_not_reached (); - } - } - + if (ypos < 0) + ypos = 0; - if (overlay->bitmap.buffer) { + if (overlay->text_image) { switch (overlay->format) { case GST_MAKE_FOURCC ('I', '4', '2', '0'): - gst_text_overlay_blit_I420 (overlay, &overlay->bitmap, + gst_text_overlay_blit_I420 (overlay, GST_BUFFER_DATA (video_frame), xpos, ypos); break; case GST_MAKE_FOURCC ('U', 'Y', 'V', 'Y'): - gst_text_overlay_blit_UYVY (overlay, &overlay->bitmap, + gst_text_overlay_blit_UYVY (overlay, GST_BUFFER_DATA (video_frame), xpos, ypos); break; default: g_assert_not_reached (); } } - return gst_pad_push (overlay->srcpad, video_frame); } diff --git a/ext/pango/gsttextoverlay.h b/ext/pango/gsttextoverlay.h index a644ef8..94ef1f8 100644 --- a/ext/pango/gsttextoverlay.h +++ b/ext/pango/gsttextoverlay.h @@ -2,7 +2,7 @@ #define __GST_TEXT_OVERLAY_H__ #include -#include +#include G_BEGIN_DECLS @@ -126,15 +126,20 @@ struct _GstTextOverlay { gboolean wait_text; PangoLayout *layout; - FT_Bitmap bitmap; - gint bitmap_buffer_size; + gdouble shadow_offset; + gdouble outline_offset; + guchar *text_image; + gint image_width; + gint image_height; gint baseline_y; + gboolean auto_adjust_size; gboolean need_render; gint shading_value; /* for timeoverlay subclass */ gboolean have_pango_markup; + gboolean use_vertical_render; }; struct _GstTextOverlayClass { diff --git a/ext/pango/gsttextrender.c b/ext/pango/gsttextrender.c index 175e520..fd8cc72 100644 --- a/ext/pango/gsttextrender.c +++ b/ext/pango/gsttextrender.c @@ -1,6 +1,7 @@ /* GStreamer * Copyright (C) <1999> Erik Walthinsen * Copyright (C) <2003> David Schleef + * Copyright (C) <2009> Young-Ho Cha * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -46,10 +47,13 @@ #include #include "gsttextrender.h" +#include GST_DEBUG_CATEGORY_EXTERN (pango_debug); #define GST_CAT_DEFAULT pango_debug +#define MINIMUM_OUTLINE_OFFSET 1.0 + static const GstElementDetails text_render_details = GST_ELEMENT_DETAILS ("Text renderer", "Filter/Editor/Video", @@ -150,6 +154,9 @@ gst_text_render_line_align_get_type (void) return text_render_line_align_type; } +static void gst_text_render_adjust_values_with_fontdesc (GstTextRender * + render, PangoFontDescription * desc); + GST_BOILERPLATE (GstTextRender, gst_text_render, GstElement, GST_TYPE_ELEMENT); static void gst_text_render_finalize (GObject * object); @@ -176,6 +183,7 @@ gst_text_render_class_init (GstTextRenderClass * klass) { GObjectClass *gobject_class; GstElementClass *gstelement_class; + PangoFontMap *fontmap; gobject_class = (GObjectClass *) klass; gstelement_class = (GstElementClass *) klass; @@ -186,7 +194,9 @@ gst_text_render_class_init (GstTextRenderClass * klass) gobject_class->set_property = gst_text_render_set_property; gobject_class->get_property = gst_text_render_get_property; - klass->pango_context = pango_ft2_get_context (72, 72); + fontmap = pango_cairo_font_map_get_default (); + klass->pango_context = + pango_cairo_font_map_create_context (PANGO_CAIRO_FONT_MAP (fontmap)); g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_FONT_DESC, g_param_spec_string ("font-desc", "font description", "Pango font description of font " @@ -217,47 +227,85 @@ gst_text_render_class_init (GstTextRenderClass * klass) G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); } - static void -resize_bitmap (GstTextRender * render, gint width, gint height) +gst_text_render_adjust_values_with_fontdesc (GstTextRender * render, + PangoFontDescription * desc) { - FT_Bitmap *bitmap = &render->bitmap; - gint pitch = (width | 3) + 1; - gint size = pitch * height; - - /* no need to keep reallocating; just keep the maximum size so far */ - if (size <= render->bitmap_buffer_size) { - bitmap->rows = height; - bitmap->width = width; - bitmap->pitch = pitch; - memset (bitmap->buffer, 0, render->bitmap_buffer_size); - return; - } - if (!bitmap->buffer) { - /* initialize */ - bitmap->pixel_mode = ft_pixel_mode_grays; - bitmap->num_grays = 256; - } - if (bitmap->buffer) - bitmap->buffer = g_realloc (bitmap->buffer, size); - else - bitmap->buffer = g_malloc (size); - bitmap->rows = height; - bitmap->width = width; - bitmap->pitch = pitch; - memset (bitmap->buffer, 0, size); - render->bitmap_buffer_size = size; + gint font_size = pango_font_description_get_size (desc) / PANGO_SCALE; + + render->shadow_offset = (double) (font_size) / 13.0; + render->outline_offset = (double) (font_size) / 15.0; + if (render->outline_offset < MINIMUM_OUTLINE_OFFSET) + render->outline_offset = MINIMUM_OUTLINE_OFFSET; } static void -gst_text_render_render_text (GstTextRender * render) +gst_text_render_render_pangocairo (GstTextRender * render) { + cairo_t *cr; + cairo_surface_t *surface; + cairo_t *cr_shadow; + cairo_surface_t *surface_shadow; PangoRectangle ink_rect, logical_rect; + gint width, height; pango_layout_get_pixel_extents (render->layout, &ink_rect, &logical_rect); - resize_bitmap (render, ink_rect.width, ink_rect.height + ink_rect.y); - pango_ft2_render_layout (&render->bitmap, render->layout, -ink_rect.x, 0); - render->baseline_y = ink_rect.y; + + width = logical_rect.width + render->shadow_offset; + height = logical_rect.height + logical_rect.y + render->shadow_offset; + + surface_shadow = cairo_image_surface_create (CAIRO_FORMAT_A8, width, height); + cr_shadow = cairo_create (surface_shadow); + + /* clear shadow surface */ + cairo_set_source_rgba (cr_shadow, 0.0, 0.0, 0.0, 0.0); + cairo_set_operator (cr_shadow, CAIRO_OPERATOR_CLEAR); + cairo_paint (cr_shadow); + cairo_set_operator (cr_shadow, CAIRO_OPERATOR_OVER); + + cairo_set_source_rgb (cr_shadow, 0.0, 0.0, 0.0); + pango_cairo_update_layout (cr_shadow, render->layout); + + /* draw shadow text */ + cairo_save (cr_shadow); + cairo_set_source_rgba (cr_shadow, 0.0, 0.0, 0.0, 0.5); + cairo_translate (cr_shadow, render->shadow_offset, render->shadow_offset); + pango_cairo_show_layout (cr_shadow, render->layout); + cairo_restore (cr_shadow); + + /* draw outline text */ + cairo_save (cr_shadow); + cairo_set_line_width (cr_shadow, render->outline_offset); + pango_cairo_layout_path (cr_shadow, render->layout); + cairo_stroke (cr_shadow); + cairo_restore (cr_shadow); + + cairo_destroy (cr_shadow); + + render->text_image = g_realloc (render->text_image, 4 * width * height); + memset (render->text_image, 0, 4 * width * height); + + surface = cairo_image_surface_create_for_data (render->text_image, + CAIRO_FORMAT_ARGB32, width, height, width * 4); + cr = cairo_create (surface); + + /* set default color */ + cairo_set_source_rgb (cr, 1.0, 1.0, 1.0); + + /* draw text */ + pango_cairo_update_layout (cr, render->layout); + pango_cairo_show_layout (cr, render->layout); + + /* composite shadow with offset */ + cairo_set_operator (cr, CAIRO_OPERATOR_DEST_OVER); + cairo_set_source_surface (cr, surface_shadow, 0.0, 0.0); + cairo_paint (cr); + + cairo_destroy (cr); + cairo_surface_destroy (surface_shadow); + cairo_surface_destroy (surface); + render->image_width = width; + render->image_height = height; } static void @@ -306,7 +354,7 @@ gst_text_render_setcaps (GstPad * pad, GstCaps * caps) GST_DEBUG ("Got caps %" GST_PTR_FORMAT, caps); - if (width >= render->bitmap.width && height >= render->bitmap.rows) { + if (width >= render->image_width && height >= render->image_height) { render->width = width; render->height = height; ret = TRUE; @@ -325,92 +373,62 @@ gst_text_render_fixate_caps (GstPad * pad, GstCaps * caps) GstStructure *s = gst_caps_get_structure (caps, 0); GST_DEBUG ("Fixating caps %" GST_PTR_FORMAT, caps); - gst_structure_fixate_field_nearest_int (s, "width", render->width); - gst_structure_fixate_field_nearest_int (s, "height", render->height); + gst_structure_fixate_field_nearest_int (s, "width", render->image_width); + gst_structure_fixate_field_nearest_int (s, "height", render->image_height); GST_DEBUG ("Fixated to %" GST_PTR_FORMAT, caps); gst_object_unref (render); } static void -gst_text_renderer_bitmap_to_ayuv (GstTextRender * render, FT_Bitmap * bitmap, - guchar * pixbuf, gint x0, gint x1, gint y0, gint y1) +gst_text_renderer_image_to_ayuv (GstTextRender * render, guchar * pixbuf, + int xpos, int ypos, int stride) { int y; /* text bitmap coordinates */ - int rowinc, bit_rowinc; guchar *p, *bitp; - guchar v; - - x0 = CLAMP (x0, 0, render->width); - x1 = CLAMP (x1, 0, render->width); - - y0 = CLAMP (y0, 0, render->height); - y1 = CLAMP (y1, 0, render->height); - - - rowinc = render->width - bitmap->width; - bit_rowinc = bitmap->pitch - bitmap->width; + guchar a, r, g, b; + int width, height; - bitp = bitmap->buffer; - p = pixbuf + ((x0 + (render->width * y0)) * 4); + width = render->image_width; + height = render->image_height; + bitp = render->text_image; - for (y = y0; y < y1; y++) { + for (y = 0; y < height; y++) { int n; - - for (n = x0; n < x1; n++) { - v = *bitp; - if (v) { - p[0] = v; - p[1] = 255; - p[2] = 0x80; - p[3] = 0x80; - } - p += 4; - bitp++; + p = pixbuf + ypos * stride + xpos; + for (n = 0; n < width; n++) { + b = *bitp++; + g = *bitp++; + r = *bitp++; + a = *bitp++; + + *p++ = a; + *p++ = CLAMP ((int) (((19595 * r) >> 16) + ((38470 * g) >> 16) + + ((7471 * b) >> 16)), 0, 255); + *p++ = CLAMP ((int) (-((11059 * r) >> 16) - ((21709 * g) >> 16) + + ((32768 * b) >> 16) + 128), 0, 255); + *p++ = CLAMP ((int) (((32768 * r) >> 16) - ((27439 * g) >> 16) - + ((5329 * b) >> 16) + 128), 0, 255); } - p += rowinc * 4; - bitp += bit_rowinc; } } static void -gst_text_renderer_bitmap_to_argb (GstTextRender * render, FT_Bitmap * bitmap, - guchar * pixbuf, gint x0, gint x1, gint y0, gint y1) +gst_text_renderer_image_to_argb (GstTextRender * render, guchar * pixbuf, + int xpos, int ypos, int stride) { - int y; /* text bitmap coordinates */ - int rowinc, bit_rowinc; + int i; guchar *p, *bitp; - guchar v; - - x0 = CLAMP (x0, 0, render->width); - x1 = CLAMP (x1, 0, render->width); - - y0 = CLAMP (y0, 0, render->height); - y1 = CLAMP (y1, 0, render->height); - + int width, height; - rowinc = render->width - bitmap->width; - bit_rowinc = bitmap->pitch - bitmap->width; + width = render->image_width; + height = render->image_height; + bitp = render->text_image; - bitp = bitmap->buffer; - p = pixbuf + ((x0 + (render->width * y0)) * 4); - - for (y = y0; y < y1; y++) { - int n; - - for (n = x0; n < x1; n++) { - v = *bitp; - if (v) { - p[0] = v; - p[1] = 255; - p[2] = 255; - p[3] = 255; - } - p += 4; - bitp++; - } - p += rowinc * 4; - bitp += bit_rowinc; + for (i = 0; i < height; i++) { + p = pixbuf + ypos * stride + xpos; + memcpy (p, bitp, width * 4); + bitp += width * 4; } } @@ -438,7 +456,7 @@ gst_text_render_chain (GstPad * pad, GstBuffer * inbuf) /* render text */ GST_DEBUG ("rendering '%*s'", size, data); pango_layout_set_markup (render->layout, (gchar *) data, size); - gst_text_render_render_text (render); + gst_text_render_render_pangocairo (render); gst_text_render_check_argb (render); @@ -487,10 +505,10 @@ gst_text_render_chain (GstPad * pad, GstBuffer * inbuf) xpos = render->xpad; break; case GST_TEXT_RENDER_HALIGN_CENTER: - xpos = (render->width - render->bitmap.width) / 2; + xpos = (render->width - render->image_width) / 2; break; case GST_TEXT_RENDER_HALIGN_RIGHT: - xpos = render->width - render->bitmap.width - render->xpad; + xpos = render->width - render->image_width - render->xpad; break; default: xpos = 0; @@ -498,10 +516,10 @@ gst_text_render_chain (GstPad * pad, GstBuffer * inbuf) switch (render->valign) { case GST_TEXT_RENDER_VALIGN_BOTTOM: - ypos = render->height - render->bitmap.rows - render->ypad; + ypos = render->height - render->image_height - render->ypad; break; case GST_TEXT_RENDER_VALIGN_BASELINE: - ypos = render->height - (render->bitmap.rows + render->ypad); + ypos = render->height - (render->image_height + render->ypad); break; case GST_TEXT_RENDER_VALIGN_TOP: ypos = render->ypad; @@ -511,13 +529,13 @@ gst_text_render_chain (GstPad * pad, GstBuffer * inbuf) break; } - if (render->bitmap.buffer) { + if (render->text_image) { if (render->use_ARGB) { - gst_text_renderer_bitmap_to_argb (render, &render->bitmap, data, xpos, - xpos + render->bitmap.width, ypos, ypos + render->bitmap.rows); + gst_text_renderer_image_to_argb (render, data, xpos, ypos, + render->width * 4); } else { - gst_text_renderer_bitmap_to_ayuv (render, &render->bitmap, data, xpos, - xpos + render->bitmap.width, ypos, ypos + render->bitmap.rows); + gst_text_renderer_image_to_ayuv (render, data, xpos, ypos, + render->width * 4); } } @@ -536,7 +554,7 @@ gst_text_render_finalize (GObject * object) { GstTextRender *render = GST_TEXT_RENDER (object); - g_free (render->bitmap.buffer); + g_free (render->text_image); if (render->layout) g_object_unref (render->layout); @@ -573,7 +591,6 @@ gst_text_render_init (GstTextRender * render, GstTextRenderClass * klass) pango_layout_new (GST_TEXT_RENDER_GET_CLASS (render)->pango_context); pango_layout_set_alignment (render->layout, (PangoAlignment) render->line_align); - memset (&render->bitmap, 0, sizeof (render->bitmap)); render->halign = DEFAULT_PROP_HALIGNMENT; render->valign = DEFAULT_PROP_VALIGNMENT; @@ -619,8 +636,9 @@ gst_text_render_set_property (GObject * object, guint prop_id, GST_LOG ("font description set: %s", g_value_get_string (value)); GST_OBJECT_LOCK (render); pango_layout_set_font_description (render->layout, desc); + gst_text_render_adjust_values_with_fontdesc (render, desc); pango_font_description_free (desc); - gst_text_render_render_text (render); + gst_text_render_render_pangocairo (render); GST_OBJECT_UNLOCK (render); } else { GST_WARNING ("font description parse failed: %s", diff --git a/ext/pango/gsttextrender.h b/ext/pango/gsttextrender.h index 11a1eb0..66d37db 100644 --- a/ext/pango/gsttextrender.h +++ b/ext/pango/gsttextrender.h @@ -2,7 +2,7 @@ #define __GST_TEXT_RENDER_H__ #include -#include +#include G_BEGIN_DECLS @@ -75,8 +75,11 @@ struct _GstTextRender { gint width; gint height; PangoLayout *layout; - FT_Bitmap bitmap; - gint bitmap_buffer_size; + gdouble shadow_offset; + gdouble outline_offset; + guchar *text_image; + gint image_width; + gint image_height; gint baseline_y; gboolean use_ARGB;