0c988ea9b12c184b7e808f4fba9d226db3f0600f
[framework/multimedia/gst-plugins-base0.10.git] / ext / pango / gsttextrender.c
1 /* GStreamer
2  * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
3  * Copyright (C) <2003> David Schleef <ds@schleef.org>
4  * Copyright (C) <2009> Young-Ho Cha <ganadist@gmail.com>
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Library General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Library General Public License for more details.
15  *
16  * You should have received a copy of the GNU Library General Public
17  * License along with this library; if not, write to the
18  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19  * Boston, MA 02111-1307, USA.
20  */
21
22
23 /**
24  * SECTION:element-textrender
25  * @see_also: #GstTextOverlay
26  *
27  * This plugin renders text received on the text sink pad to a video
28  * buffer (retaining the alpha channel), so it can later be overlayed
29  * on top of video streams using other elements.
30  *
31  * The text can contain newline characters. (FIXME: What about text 
32  * wrapping? It does not make sense in this context)
33  *
34  * <refsect2>
35  * <title>Example launch lines</title>
36  * |[
37  * gst-launch -v filesrc location=subtitles.srt ! subparse ! textrender ! xvimagesink
38  * ]|
39  * </refsect2>
40  */
41
42 #ifdef HAVE_CONFIG_H
43 #include <config.h>
44 #endif
45
46 #include <gst/gst.h>
47 #include <gst/video/video.h>
48
49 #include "gsttextrender.h"
50 #include <string.h>
51
52 #if G_BYTE_ORDER == G_LITTLE_ENDIAN
53 # define CAIRO_ARGB_A 3
54 # define CAIRO_ARGB_R 2
55 # define CAIRO_ARGB_G 1
56 # define CAIRO_ARGB_B 0
57 #else
58 # define CAIRO_ARGB_A 0
59 # define CAIRO_ARGB_R 1
60 # define CAIRO_ARGB_G 2
61 # define CAIRO_ARGB_B 3
62 #endif
63
64 GST_DEBUG_CATEGORY_EXTERN (pango_debug);
65 #define GST_CAT_DEFAULT pango_debug
66
67 #define MINIMUM_OUTLINE_OFFSET 1.0
68
69 #define DEFAULT_PROP_VALIGNMENT GST_TEXT_RENDER_VALIGN_BASELINE
70 #define DEFAULT_PROP_HALIGNMENT GST_TEXT_RENDER_HALIGN_CENTER
71 #define DEFAULT_PROP_LINE_ALIGNMENT GST_TEXT_RENDER_LINE_ALIGN_CENTER
72 #define DEFAULT_PROP_XPAD       25
73 #define DEFAULT_PROP_YPAD       25
74 #ifdef GST_EXT_TEXTRENDER_ENHANCEMENT
75 #define DEFAULT_PROP_RENDER_WIDTH 800
76 #define DEFAULT_PROP_RENDER_HEIGHT 480
77 #define DEFAULT_PROP_RENDER_SILENT FALSE
78 #else
79 #define DEFAULT_RENDER_WIDTH 720
80 #define DEFAULT_RENDER_HEIGHT 576
81 #endif
82
83 enum
84 {
85   PROP_0,
86   PROP_HALIGNMENT,
87   PROP_VALIGNMENT,
88   PROP_LINE_ALIGNMENT,
89   PROP_XPAD,
90   PROP_YPAD,
91 #ifdef GST_EXT_TEXTRENDER_ENHANCEMENT
92   PROP_RENDER_WIDTH,
93   PROP_RENDER_HEIGHT,
94   PROP_RENDER_SILENT,
95 #endif
96   PROP_FONT_DESC
97 };
98
99
100 static GstStaticPadTemplate src_template_factory =
101     GST_STATIC_PAD_TEMPLATE ("src",
102     GST_PAD_SRC,
103     GST_PAD_ALWAYS,
104     GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("AYUV") ";" GST_VIDEO_CAPS_ARGB)
105     );
106
107 static GstStaticPadTemplate sink_template_factory =
108     GST_STATIC_PAD_TEMPLATE ("sink",
109     GST_PAD_SINK,
110     GST_PAD_ALWAYS,
111     GST_STATIC_CAPS ("text/x-pango-markup; text/plain")
112     );
113
114 #define GST_TYPE_TEXT_RENDER_VALIGN (gst_text_render_valign_get_type())
115 static GType
116 gst_text_render_valign_get_type (void)
117 {
118   static GType text_render_valign_type = 0;
119   static const GEnumValue text_render_valign[] = {
120     {GST_TEXT_RENDER_VALIGN_BASELINE, "baseline", "baseline"},
121     {GST_TEXT_RENDER_VALIGN_BOTTOM, "bottom", "bottom"},
122     {GST_TEXT_RENDER_VALIGN_TOP, "top", "top"},
123     {0, NULL, NULL},
124   };
125
126   if (!text_render_valign_type) {
127     text_render_valign_type =
128         g_enum_register_static ("GstTextRenderVAlign", text_render_valign);
129   }
130   return text_render_valign_type;
131 }
132
133 #define GST_TYPE_TEXT_RENDER_HALIGN (gst_text_render_halign_get_type())
134 static GType
135 gst_text_render_halign_get_type (void)
136 {
137   static GType text_render_halign_type = 0;
138   static const GEnumValue text_render_halign[] = {
139     {GST_TEXT_RENDER_HALIGN_LEFT, "left", "left"},
140     {GST_TEXT_RENDER_HALIGN_CENTER, "center", "center"},
141     {GST_TEXT_RENDER_HALIGN_RIGHT, "right", "right"},
142     {0, NULL, NULL},
143   };
144
145   if (!text_render_halign_type) {
146     text_render_halign_type =
147         g_enum_register_static ("GstTextRenderHAlign", text_render_halign);
148   }
149   return text_render_halign_type;
150 }
151
152 #define GST_TYPE_TEXT_RENDER_LINE_ALIGN (gst_text_render_line_align_get_type())
153 static GType
154 gst_text_render_line_align_get_type (void)
155 {
156   static GType text_render_line_align_type = 0;
157   static const GEnumValue text_render_line_align[] = {
158     {GST_TEXT_RENDER_LINE_ALIGN_LEFT, "left", "left"},
159     {GST_TEXT_RENDER_LINE_ALIGN_CENTER, "center", "center"},
160     {GST_TEXT_RENDER_LINE_ALIGN_RIGHT, "right", "right"},
161     {0, NULL, NULL}
162   };
163
164   if (!text_render_line_align_type) {
165     text_render_line_align_type =
166         g_enum_register_static ("GstTextRenderLineAlign",
167         text_render_line_align);
168   }
169   return text_render_line_align_type;
170 }
171
172 static void gst_text_render_adjust_values_with_fontdesc (GstTextRender *
173     render, PangoFontDescription * desc);
174
175 GST_BOILERPLATE (GstTextRender, gst_text_render, GstElement, GST_TYPE_ELEMENT);
176
177 static void gst_text_render_finalize (GObject * object);
178 static void gst_text_render_set_property (GObject * object,
179     guint prop_id, const GValue * value, GParamSpec * pspec);
180 static void gst_text_render_get_property (GObject * object,
181     guint prop_id, GValue * value, GParamSpec * pspec);
182
183 static void
184 gst_text_render_base_init (gpointer g_class)
185 {
186   GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
187
188   gst_element_class_add_pad_template (element_class,
189       gst_static_pad_template_get (&src_template_factory));
190   gst_element_class_add_pad_template (element_class,
191       gst_static_pad_template_get (&sink_template_factory));
192
193   gst_element_class_set_details_simple (element_class, "Text renderer",
194       "Filter/Editor/Video",
195       "Renders a text string to an image bitmap",
196       "David Schleef <ds@schleef.org>, "
197       "GStreamer maintainers <gstreamer-devel@lists.sourceforge.net>");
198 }
199
200 static void
201 gst_text_render_class_init (GstTextRenderClass * klass)
202 {
203   GObjectClass *gobject_class;
204   PangoFontMap *fontmap;
205
206   gobject_class = (GObjectClass *) klass;
207
208   parent_class = g_type_class_peek_parent (klass);
209
210   gobject_class->finalize = gst_text_render_finalize;
211   gobject_class->set_property = gst_text_render_set_property;
212   gobject_class->get_property = gst_text_render_get_property;
213
214   fontmap = pango_cairo_font_map_get_default ();
215   klass->pango_context =
216       pango_cairo_font_map_create_context (PANGO_CAIRO_FONT_MAP (fontmap));
217   g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_FONT_DESC,
218       g_param_spec_string ("font-desc", "font description",
219           "Pango font description of font "
220           "to be used for rendering. "
221           "See documentation of "
222           "pango_font_description_from_string"
223           " for syntax.", "", G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS));
224   g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_VALIGNMENT,
225       g_param_spec_enum ("valignment", "vertical alignment",
226           "Vertical alignment of the text", GST_TYPE_TEXT_RENDER_VALIGN,
227           DEFAULT_PROP_VALIGNMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
228   g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_HALIGNMENT,
229       g_param_spec_enum ("halignment", "horizontal alignment",
230           "Horizontal alignment of the text", GST_TYPE_TEXT_RENDER_HALIGN,
231           DEFAULT_PROP_HALIGNMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
232   g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_XPAD,
233       g_param_spec_int ("xpad", "horizontal paddding",
234           "Horizontal paddding when using left/right alignment", 0, G_MAXINT,
235           DEFAULT_PROP_XPAD, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
236   g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_YPAD,
237       g_param_spec_int ("ypad", "vertical padding",
238           "Vertical padding when using top/bottom alignment", 0, G_MAXINT,
239           DEFAULT_PROP_YPAD, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
240 #ifdef GST_EXT_TEXTRENDER_ENHANCEMENT
241   g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_RENDER_WIDTH,
242       g_param_spec_int ("width", "render width",
243           "Width of rendered video size", 0, G_MAXINT,
244           DEFAULT_PROP_RENDER_WIDTH, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
245   g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_RENDER_HEIGHT,
246       g_param_spec_int ("height", "render height",
247           "Height of rendered video size", 0, G_MAXINT,
248           DEFAULT_PROP_RENDER_HEIGHT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
249   g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_RENDER_SILENT,
250       g_param_spec_boolean ("silent", "render silent",
251           "Whether to render the text string", 
252           DEFAULT_PROP_RENDER_SILENT, G_PARAM_READWRITE));
253 #endif
254   g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_LINE_ALIGNMENT,
255       g_param_spec_enum ("line-alignment", "line alignment",
256           "Alignment of text lines relative to each other.",
257           GST_TYPE_TEXT_RENDER_LINE_ALIGN, DEFAULT_PROP_LINE_ALIGNMENT,
258           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
259 }
260
261 static void
262 gst_text_render_adjust_values_with_fontdesc (GstTextRender * render,
263     PangoFontDescription * desc)
264 {
265   gint font_size = pango_font_description_get_size (desc) / PANGO_SCALE;
266
267   render->shadow_offset = (double) (font_size) / 13.0;
268   render->outline_offset = (double) (font_size) / 15.0;
269   if (render->outline_offset < MINIMUM_OUTLINE_OFFSET)
270     render->outline_offset = MINIMUM_OUTLINE_OFFSET;
271 }
272
273 static void
274 gst_text_render_render_pangocairo (GstTextRender * render)
275 {
276   cairo_t *cr;
277   cairo_surface_t *surface;
278   cairo_t *cr_shadow;
279   cairo_surface_t *surface_shadow;
280   PangoRectangle ink_rect, logical_rect;
281   gint width, height;
282
283   pango_layout_get_pixel_extents (render->layout, &ink_rect, &logical_rect);
284
285   width = logical_rect.width + render->shadow_offset;
286   height = logical_rect.height + logical_rect.y + render->shadow_offset;
287
288   surface_shadow = cairo_image_surface_create (CAIRO_FORMAT_A8, width, height);
289   cr_shadow = cairo_create (surface_shadow);
290
291   /* clear shadow surface */
292   cairo_set_operator (cr_shadow, CAIRO_OPERATOR_CLEAR);
293   cairo_paint (cr_shadow);
294   cairo_set_operator (cr_shadow, CAIRO_OPERATOR_OVER);
295
296   /* draw shadow text */
297   cairo_save (cr_shadow);
298   cairo_set_source_rgba (cr_shadow, 0.0, 0.0, 0.0, 0.5);
299   cairo_translate (cr_shadow, render->shadow_offset, render->shadow_offset);
300   pango_cairo_show_layout (cr_shadow, render->layout);
301   cairo_restore (cr_shadow);
302
303   /* draw outline text */
304   cairo_save (cr_shadow);
305   cairo_set_source_rgb (cr_shadow, 0.0, 0.0, 0.0);
306   cairo_set_line_width (cr_shadow, render->outline_offset);
307   pango_cairo_layout_path (cr_shadow, render->layout);
308   cairo_stroke (cr_shadow);
309   cairo_restore (cr_shadow);
310
311   cairo_destroy (cr_shadow);
312
313   render->text_image = g_realloc (render->text_image, 4 * width * height);
314
315   surface = cairo_image_surface_create_for_data (render->text_image,
316       CAIRO_FORMAT_ARGB32, width, height, width * 4);
317   cr = cairo_create (surface);
318   cairo_set_operator (cr, CAIRO_OPERATOR_CLEAR);
319   cairo_paint (cr);
320   cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
321
322   /* set default color */
323   cairo_set_source_rgb (cr, 1.0, 1.0, 1.0);
324
325   cairo_save (cr);
326   /* draw text */
327   pango_cairo_show_layout (cr, render->layout);
328   cairo_restore (cr);
329
330   /* composite shadow with offset */
331   cairo_set_operator (cr, CAIRO_OPERATOR_DEST_OVER);
332   cairo_set_source_surface (cr, surface_shadow, 0.0, 0.0);
333   cairo_paint (cr);
334
335   cairo_destroy (cr);
336   cairo_surface_destroy (surface_shadow);
337   cairo_surface_destroy (surface);
338   render->image_width = width;
339   render->image_height = height;
340 }
341
342 static void
343 gst_text_render_check_argb (GstTextRender * render)
344 {
345   GstCaps *peer_caps;
346   peer_caps = gst_pad_get_allowed_caps (render->srcpad);
347   if (G_LIKELY (peer_caps)) {
348     guint i = 0, n = 0;
349
350     n = gst_caps_get_size (peer_caps);
351     GST_DEBUG_OBJECT (render, "peer allowed caps (%u structure(s)) are %"
352         GST_PTR_FORMAT, n, peer_caps);
353
354     /* Check if AYUV or ARGB is first */
355     for (i = 0; i < n; i++) {
356       GstStructure *s = gst_caps_get_structure (peer_caps, i);
357       if (gst_structure_has_name (s, "video/x-raw-rgb") &&
358           gst_structure_has_field (s, "alpha_mask")) {
359         render->use_ARGB = TRUE;
360         break;
361       } else if (gst_structure_has_name (s, "video/x-raw-yuv")) {
362         guint fourcc;
363         if (gst_structure_get_fourcc (s, "format", &fourcc) &&
364             fourcc == GST_MAKE_FOURCC ('A', 'Y', 'U', 'V')) {
365           render->use_ARGB = FALSE;
366           break;
367         }
368       }
369     }
370     gst_caps_unref (peer_caps);
371   }
372 }
373
374 static gboolean
375 gst_text_render_setcaps (GstPad * pad, GstCaps * caps)
376 {
377   GstTextRender *render = GST_TEXT_RENDER (gst_pad_get_parent (pad));
378   GstStructure *structure;
379   gboolean ret = FALSE;
380   gint width = 0, height = 0;
381
382   structure = gst_caps_get_structure (caps, 0);
383   gst_structure_get_int (structure, "width", &width);
384   gst_structure_get_int (structure, "height", &height);
385
386   GST_DEBUG ("Got caps %" GST_PTR_FORMAT, caps);
387
388   if (width >= render->image_width && height >= render->image_height) {
389     render->width = width;
390     render->height = height;
391     ret = TRUE;
392   }
393
394   gst_text_render_check_argb (render);
395
396   gst_object_unref (render);
397   return ret;
398 }
399
400 static void
401 gst_text_render_fixate_caps (GstPad * pad, GstCaps * caps)
402 {
403   GstTextRender *render = GST_TEXT_RENDER (gst_pad_get_parent (pad));
404   GstStructure *s = gst_caps_get_structure (caps, 0);
405
406   GST_DEBUG ("Fixating caps %" GST_PTR_FORMAT, caps);
407   gst_structure_fixate_field_nearest_int (s, "width", render->image_width);
408   gst_structure_fixate_field_nearest_int (s, "height", render->image_height);
409   GST_DEBUG ("Fixated to    %" GST_PTR_FORMAT, caps);
410
411   gst_object_unref (render);
412 }
413
414 #define CAIRO_UNPREMULTIPLY(a,r,g,b) G_STMT_START { \
415   b = (a > 0) ? MIN ((b * 255 + a / 2) / a, 255) : 0; \
416   g = (a > 0) ? MIN ((g * 255 + a / 2) / a, 255) : 0; \
417   r = (a > 0) ? MIN ((r * 255 + a / 2) / a, 255) : 0; \
418 } G_STMT_END
419
420 static void
421 gst_text_renderer_image_to_ayuv (GstTextRender * render, guchar * pixbuf,
422     int xpos, int ypos, int stride)
423 {
424   int y;                        /* text bitmap coordinates */
425   guchar *p, *bitp;
426   guchar a, r, g, b;
427   int width, height;
428
429   width = render->image_width;
430   height = render->image_height;
431   bitp = render->text_image;
432
433   for (y = 0; y < height; y++) {
434     int n;
435     p = pixbuf + (ypos + y) * stride + xpos * 4;
436     for (n = 0; n < width; n++) {
437       b = bitp[CAIRO_ARGB_B];
438       g = bitp[CAIRO_ARGB_G];
439       r = bitp[CAIRO_ARGB_R];
440       a = bitp[CAIRO_ARGB_A];
441       bitp += 4;
442
443       /* Cairo uses pre-multiplied ARGB, unpremultiply it */
444       CAIRO_UNPREMULTIPLY (a, r, g, b);
445
446       *p++ = a;
447       *p++ = CLAMP ((int) (((19595 * r) >> 16) + ((38470 * g) >> 16) +
448               ((7471 * b) >> 16)), 0, 255);
449       *p++ = CLAMP ((int) (-((11059 * r) >> 16) - ((21709 * g) >> 16) +
450               ((32768 * b) >> 16) + 128), 0, 255);
451       *p++ = CLAMP ((int) (((32768 * r) >> 16) - ((27439 * g) >> 16) -
452               ((5329 * b) >> 16) + 128), 0, 255);
453     }
454   }
455 }
456
457 static void
458 gst_text_renderer_image_to_argb (GstTextRender * render, guchar * pixbuf,
459     int xpos, int ypos, int stride)
460 {
461   int i, j;
462   guchar *p, *bitp;
463   int width, height;
464
465   width = render->image_width;
466   height = render->image_height;
467   bitp = render->text_image;
468
469   for (i = 0; i < height; i++) {
470     p = pixbuf + (ypos + i) * stride + xpos * 4;
471     for (j = 0; j < width; j++) {
472       p[0] = bitp[CAIRO_ARGB_A];
473       p[1] = bitp[CAIRO_ARGB_R];
474       p[2] = bitp[CAIRO_ARGB_G];
475       p[3] = bitp[CAIRO_ARGB_B];
476
477       /* Cairo uses pre-multiplied ARGB, unpremultiply it */
478       CAIRO_UNPREMULTIPLY (p[0], p[1], p[2], p[3]);
479
480       bitp += 4;
481       p += 4;
482     }
483   }
484 }
485
486 static GstFlowReturn
487 gst_text_render_chain (GstPad * pad, GstBuffer * inbuf)
488 {
489   GstTextRender *render;
490   GstFlowReturn ret;
491   GstBuffer *outbuf;
492   GstCaps *caps = NULL;
493   guint8 *data = GST_BUFFER_DATA (inbuf);
494   guint size = GST_BUFFER_SIZE (inbuf);
495   gint n;
496   gint xpos, ypos;
497
498   render = GST_TEXT_RENDER (gst_pad_get_parent (pad));
499
500   /* somehow pango barfs over "\0" buffers... */
501   while (size > 0 &&
502       (data[size - 1] == '\r' ||
503           data[size - 1] == '\n' || data[size - 1] == '\0')) {
504     size--;
505   }
506
507   /* render text */
508 #ifdef GST_EXT_TEXTRENDER_ENHANCEMENT
509   if (!render->silent) {
510     GST_DEBUG ("rendering '%*s'", size, data);
511     pango_layout_set_markup (render->layout, (gchar *) data, size);
512     gst_text_render_render_pangocairo (render);
513   } else {
514     GST_DEBUG ("silent mode is on, not rendering '%*s'", size, data);
515   }
516 #else
517   GST_DEBUG ("rendering '%*s'", size, data);
518   pango_layout_set_markup (render->layout, (gchar *) data, size);
519   gst_text_render_render_pangocairo (render);
520 #endif
521
522   gst_text_render_check_argb (render);
523
524   if (!render->use_ARGB) {
525     caps =
526         gst_video_format_new_caps (GST_VIDEO_FORMAT_AYUV, render->width,
527         render->height, 1, 1, 1, 1);
528   } else {
529     caps =
530         gst_video_format_new_caps (GST_VIDEO_FORMAT_ARGB, render->width,
531         render->height, 1, 1, 1, 1);
532   }
533
534   if (!gst_pad_set_caps (render->srcpad, caps)) {
535     gst_caps_unref (caps);
536     GST_ELEMENT_ERROR (render, CORE, NEGOTIATION, (NULL), (NULL));
537     ret = GST_FLOW_ERROR;
538     goto done;
539   }
540
541   GST_DEBUG ("Allocating buffer WxH = %dx%d", render->width, render->height);
542   ret =
543       gst_pad_alloc_buffer_and_set_caps (render->srcpad, GST_BUFFER_OFFSET_NONE,
544       render->width * render->height * 4, caps, &outbuf);
545
546   if (ret != GST_FLOW_OK)
547     goto done;
548
549   gst_buffer_copy_metadata (outbuf, inbuf, GST_BUFFER_COPY_TIMESTAMPS);
550   data = GST_BUFFER_DATA (outbuf);
551
552   if (render->use_ARGB) {
553     memset (data, 0, render->width * render->height * 4);
554   } else {
555     for (n = 0; n < render->width * render->height; n++) {
556       data[n * 4] = data[n * 4 + 1] = 0;
557       data[n * 4 + 2] = data[n * 4 + 3] = 128;
558     }
559   }
560
561 #ifdef GST_EXT_TEXTRENDER_ENHANCEMENT
562   if (render->silent) {
563     ret = gst_pad_push (render->srcpad, outbuf);
564     goto done;
565   }
566 #endif
567
568   switch (render->halign) {
569     case GST_TEXT_RENDER_HALIGN_LEFT:
570       xpos = render->xpad;
571       break;
572     case GST_TEXT_RENDER_HALIGN_CENTER:
573       xpos = (render->width - render->image_width) / 2;
574       break;
575     case GST_TEXT_RENDER_HALIGN_RIGHT:
576       xpos = render->width - render->image_width - render->xpad;
577       break;
578     default:
579       xpos = 0;
580   }
581
582   switch (render->valign) {
583     case GST_TEXT_RENDER_VALIGN_BOTTOM:
584       ypos = render->height - render->image_height - render->ypad;
585       break;
586     case GST_TEXT_RENDER_VALIGN_BASELINE:
587       ypos = render->height - (render->image_height + render->ypad);
588       break;
589     case GST_TEXT_RENDER_VALIGN_TOP:
590       ypos = render->ypad;
591       break;
592     default:
593       ypos = render->ypad;
594       break;
595   }
596
597   if (render->text_image) {
598     if (render->use_ARGB) {
599       gst_text_renderer_image_to_argb (render, data, xpos, ypos,
600           render->width * 4);
601     } else {
602       gst_text_renderer_image_to_ayuv (render, data, xpos, ypos,
603           render->width * 4);
604     }
605   }
606
607   ret = gst_pad_push (render->srcpad, outbuf);
608
609 done:
610   if (caps)
611     gst_caps_unref (caps);
612   gst_buffer_unref (inbuf);
613   gst_object_unref (render);
614   return ret;
615 }
616
617 static void
618 gst_text_render_finalize (GObject * object)
619 {
620   GstTextRender *render = GST_TEXT_RENDER (object);
621
622   g_free (render->text_image);
623
624   if (render->layout)
625     g_object_unref (render->layout);
626
627   G_OBJECT_CLASS (parent_class)->finalize (object);
628 }
629
630 static void
631 gst_text_render_init (GstTextRender * render, GstTextRenderClass * klass)
632 {
633   GstPadTemplate *template;
634
635   /* sink */
636   template = gst_static_pad_template_get (&sink_template_factory);
637   render->sinkpad = gst_pad_new_from_template (template, "sink");
638   gst_object_unref (template);
639   gst_pad_set_chain_function (render->sinkpad,
640       GST_DEBUG_FUNCPTR (gst_text_render_chain));
641   gst_element_add_pad (GST_ELEMENT (render), render->sinkpad);
642
643   /* source */
644   template = gst_static_pad_template_get (&src_template_factory);
645   render->srcpad = gst_pad_new_from_template (template, "src");
646   gst_object_unref (template);
647   gst_pad_set_fixatecaps_function (render->srcpad,
648       GST_DEBUG_FUNCPTR (gst_text_render_fixate_caps));
649   gst_pad_set_setcaps_function (render->srcpad,
650       GST_DEBUG_FUNCPTR (gst_text_render_setcaps));
651
652   gst_element_add_pad (GST_ELEMENT (render), render->srcpad);
653
654   render->line_align = DEFAULT_PROP_LINE_ALIGNMENT;
655   render->layout =
656       pango_layout_new (GST_TEXT_RENDER_GET_CLASS (render)->pango_context);
657   pango_layout_set_alignment (render->layout,
658       (PangoAlignment) render->line_align);
659
660   render->halign = DEFAULT_PROP_HALIGNMENT;
661   render->valign = DEFAULT_PROP_VALIGNMENT;
662   render->xpad = DEFAULT_PROP_XPAD;
663   render->ypad = DEFAULT_PROP_YPAD;
664
665 #ifdef GST_EXT_TEXTRENDER_ENHANCEMENT
666   render->width = DEFAULT_PROP_RENDER_WIDTH;
667   render->height = DEFAULT_PROP_RENDER_HEIGHT;
668   render->silent = DEFAULT_PROP_RENDER_SILENT;
669 #else
670   render->width = DEFAULT_RENDER_WIDTH;
671   render->height = DEFAULT_RENDER_HEIGHT;
672 #endif
673
674   render->use_ARGB = FALSE;
675 }
676
677 static void
678 gst_text_render_set_property (GObject * object, guint prop_id,
679     const GValue * value, GParamSpec * pspec)
680 {
681   GstTextRender *render = GST_TEXT_RENDER (object);
682
683   switch (prop_id) {
684     case PROP_VALIGNMENT:
685       render->valign = g_value_get_enum (value);
686       break;
687     case PROP_HALIGNMENT:
688       render->halign = g_value_get_enum (value);
689       break;
690     case PROP_LINE_ALIGNMENT:
691       render->line_align = g_value_get_enum (value);
692       pango_layout_set_alignment (render->layout,
693           (PangoAlignment) render->line_align);
694       break;
695     case PROP_XPAD:
696       render->xpad = g_value_get_int (value);
697       break;
698     case PROP_YPAD:
699       render->ypad = g_value_get_int (value);
700       break;
701 #ifdef GST_EXT_TEXTRENDER_ENHANCEMENT
702     case PROP_RENDER_WIDTH:
703       render->width = g_value_get_int (value);
704       break;
705     case PROP_RENDER_HEIGHT:
706       render->height = g_value_get_int (value);
707       break;
708     case PROP_RENDER_SILENT:
709       render->silent = g_value_get_boolean (value);
710       break;
711 #endif
712     case PROP_FONT_DESC:
713     {
714       PangoFontDescription *desc;
715
716       desc = pango_font_description_from_string (g_value_get_string (value));
717       if (desc) {
718         GST_LOG ("font description set: %s", g_value_get_string (value));
719         GST_OBJECT_LOCK (render);
720         pango_layout_set_font_description (render->layout, desc);
721         gst_text_render_adjust_values_with_fontdesc (render, desc);
722         pango_font_description_free (desc);
723         gst_text_render_render_pangocairo (render);
724         GST_OBJECT_UNLOCK (render);
725       } else {
726         GST_WARNING ("font description parse failed: %s",
727             g_value_get_string (value));
728       }
729       break;
730     }
731
732     default:
733       break;
734   }
735 }
736
737 static void
738 gst_text_render_get_property (GObject * object, guint prop_id,
739     GValue * value, GParamSpec * pspec)
740 {
741   GstTextRender *render = GST_TEXT_RENDER (object);
742
743   switch (prop_id) {
744     case PROP_VALIGNMENT:
745       g_value_set_enum (value, render->valign);
746       break;
747     case PROP_HALIGNMENT:
748       g_value_set_enum (value, render->halign);
749       break;
750     case PROP_LINE_ALIGNMENT:
751       g_value_set_enum (value, render->line_align);
752       break;
753     case PROP_XPAD:
754       g_value_set_int (value, render->xpad);
755       break;
756     case PROP_YPAD:
757       g_value_set_int (value, render->ypad);
758       break;
759 #ifdef GST_EXT_TEXTRENDER_ENHANCEMENT
760     case PROP_RENDER_WIDTH:
761       g_value_set_int (value, render->width);
762       break;
763     case PROP_RENDER_HEIGHT:
764       g_value_set_int (value, render->height);
765       break;
766     case PROP_RENDER_SILENT:
767       g_value_set_boolean (value, render->silent);
768       break;
769 #endif
770     default:
771       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
772       break;
773   }
774 }