ext/: update of cairo-based timeoverlay to 1.0 Cairo API doesn't work yet for resizin...
[platform/upstream/gst-plugins-good.git] / ext / cairo / gsttextoverlay.c
1 /* GStreamer
2  * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
3  * Copyright (C) <2003> David Schleef <ds@schleef.org>
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Library General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Library General Public License for more details.
14  *
15  * You should have received a copy of the GNU Library General Public
16  * License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18  * Boston, MA 02111-1307, USA.
19  */
20
21 #ifdef HAVE_CONFIG_H
22 #include <config.h>
23 #endif
24 #include <string.h>
25 #include <strings.h>
26 #include <gst/gst.h>
27 #include "gsttextoverlay.h"
28
29 static GstElementDetails textoverlay_details = {
30   "Text Overlay",
31   "Filter/Editor/Video",
32   "Adds text strings on top of a video buffer",
33   "David Schleef <ds@schleef.org>"
34 };
35
36 enum
37 {
38   ARG_0,
39   ARG_TEXT,
40   ARG_VALIGN,
41   ARG_HALIGN,
42   ARG_X0,
43   ARG_Y0,
44   ARG_FONT_DESC
45 };
46
47
48 static GstStaticPadTemplate textoverlay_src_template_factory =
49 GST_STATIC_PAD_TEMPLATE ("src",
50     GST_PAD_SRC,
51     GST_PAD_ALWAYS,
52     GST_STATIC_CAPS ("video/x-raw-yuv, "
53         "format = (fourcc) I420, "
54         "width = (int) [ 1, MAX ], " "height = (int) [ 1, MAX ]")
55     );
56
57 static GstStaticPadTemplate video_sink_template_factory =
58 GST_STATIC_PAD_TEMPLATE ("video_sink",
59     GST_PAD_SINK,
60     GST_PAD_ALWAYS,
61     GST_STATIC_CAPS ("video/x-raw-yuv, "
62         "format = (fourcc) I420, "
63         "width = (int) [ 1, MAX ], " "height = (int) [ 1, MAX ]")
64     );
65
66 static GstStaticPadTemplate text_sink_template_factory =
67 GST_STATIC_PAD_TEMPLATE ("text_sink",
68     GST_PAD_SINK,
69     GST_PAD_ALWAYS,
70     GST_STATIC_CAPS ("text/plain")
71     );
72
73 static void gst_textoverlay_base_init (gpointer g_class);
74 static void gst_textoverlay_class_init (GstTextOverlayClass * klass);
75 static void gst_textoverlay_init (GstTextOverlay * overlay);
76 static void gst_textoverlay_set_property (GObject * object,
77     guint prop_id, const GValue * value, GParamSpec * pspec);
78 static void gst_textoverlay_get_property (GObject * object,
79     guint prop_id, GValue * value, GParamSpec * pspec);
80 static GstStateChangeReturn gst_textoverlay_change_state (GstElement * element,
81     GstStateChange transition);
82 static void gst_textoverlay_finalize (GObject * object);
83
84
85 static GstElementClass *parent_class = NULL;
86
87 /*static guint gst_textoverlay_signals[LAST_SIGNAL] = { 0 }; */
88
89
90 GType
91 gst_textoverlay_get_type (void)
92 {
93   static GType textoverlay_type = 0;
94
95   if (!textoverlay_type) {
96     static const GTypeInfo textoverlay_info = {
97       sizeof (GstTextOverlayClass),
98       gst_textoverlay_base_init,
99       NULL,
100       (GClassInitFunc) gst_textoverlay_class_init,
101       NULL,
102       NULL,
103       sizeof (GstTextOverlay),
104       0,
105       (GInstanceInitFunc) gst_textoverlay_init,
106     };
107
108     textoverlay_type =
109         g_type_register_static (GST_TYPE_ELEMENT, "GstTextOverlay",
110         &textoverlay_info, 0);
111   }
112   return textoverlay_type;
113 }
114
115 static void
116 gst_textoverlay_base_init (gpointer g_class)
117 {
118   GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
119
120   gst_element_class_add_pad_template (element_class,
121       gst_static_pad_template_get (&textoverlay_src_template_factory));
122   gst_element_class_add_pad_template (element_class,
123       gst_static_pad_template_get (&video_sink_template_factory));
124   gst_element_class_add_pad_template (element_class,
125       gst_static_pad_template_get (&text_sink_template_factory));
126
127   gst_element_class_set_details (element_class, &textoverlay_details);
128 }
129
130 static void
131 gst_textoverlay_class_init (GstTextOverlayClass * klass)
132 {
133   GObjectClass *gobject_class;
134   GstElementClass *gstelement_class;
135
136   gobject_class = (GObjectClass *) klass;
137   gstelement_class = (GstElementClass *) klass;
138
139   parent_class = g_type_class_peek_parent (klass);
140
141   gobject_class->finalize = gst_textoverlay_finalize;
142   gobject_class->set_property = gst_textoverlay_set_property;
143   gobject_class->get_property = gst_textoverlay_get_property;
144
145   gstelement_class->change_state = gst_textoverlay_change_state;
146   g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_TEXT,
147       g_param_spec_string ("text", "text",
148           "Text to be display.", "", G_PARAM_WRITABLE));
149   g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_VALIGN,
150       g_param_spec_string ("valign", "vertical alignment",
151           "Vertical alignment of the text. "
152           "Can be either 'baseline', 'bottom', or 'top'",
153           "baseline", G_PARAM_WRITABLE));
154   g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_HALIGN,
155       g_param_spec_string ("halign", "horizontal alignment",
156           "Horizontal alignment of the text. "
157           "Can be either 'left', 'right', or 'center'",
158           "center", G_PARAM_WRITABLE));
159   g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_X0,
160       g_param_spec_int ("x0", "X position",
161           "Initial X position."
162           " Horizontal aligment takes this point"
163           " as reference.", G_MININT, G_MAXINT, 0, G_PARAM_WRITABLE));
164   g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_Y0,
165       g_param_spec_int ("y0", "Y position",
166           "Initial Y position."
167           " Vertical aligment takes this point"
168           " as reference.", G_MININT, G_MAXINT, 0, G_PARAM_WRITABLE));
169   g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_FONT_DESC,
170       g_param_spec_string ("font-desc", "font description",
171           "Pango font description of font "
172           "to be used for rendering. "
173           "See documentation of "
174           "pango_font_description_from_string"
175           " for syntax.", "", G_PARAM_WRITABLE));
176 }
177
178
179 #if 0
180 static void
181 resize_bitmap (GstTextOverlay * overlay, int width, int height)
182 {
183   FT_Bitmap *bitmap = &overlay->bitmap;
184   int pitch = (width | 3) + 1;
185   int size = pitch * height;
186
187   /* no need to keep reallocating; just keep the maximum size so far */
188   if (size <= overlay->bitmap_buffer_size) {
189     bitmap->rows = height;
190     bitmap->width = width;
191     bitmap->pitch = pitch;
192     memset (bitmap->buffer, 0, overlay->bitmap_buffer_size);
193     return;
194   }
195   if (!bitmap->buffer) {
196     /* initialize */
197     bitmap->pixel_mode = ft_pixel_mode_grays;
198     bitmap->num_grays = 256;
199   }
200   if (bitmap->buffer)
201     bitmap->buffer = g_realloc (bitmap->buffer, size);
202   else
203     bitmap->buffer = g_malloc (size);
204   bitmap->rows = height;
205   bitmap->width = width;
206   bitmap->pitch = pitch;
207   memset (bitmap->buffer, 0, size);
208   overlay->bitmap_buffer_size = size;
209 }
210 #endif
211
212 static void
213 gst_textoverlay_render_text (GstTextOverlay * overlay, gchar * text,
214     int textlen)
215 {
216   cairo_text_extents_t extents;
217   char *string;
218   double x, y;
219
220   string = g_strndup (text, textlen);
221
222   if (overlay->text_fill_image)
223     g_free (overlay->text_fill_image);
224   overlay->text_fill_image =
225       g_malloc (4 * overlay->width * overlay->text_height);
226   cairo_set_target_image (overlay->cr, overlay->text_fill_image,
227       CAIRO_FORMAT_ARGB32, overlay->width, overlay->text_height,
228       overlay->width * 4);
229
230   cairo_save (overlay->cr);
231   cairo_rectangle (overlay->cr, 0, 0, overlay->width, overlay->text_height);
232   cairo_set_rgb_color (overlay->cr, 0, 0, 0);
233   cairo_set_alpha (overlay->cr, 1.0);
234   cairo_set_operator (overlay->cr, CAIRO_OPERATOR_SRC);
235   cairo_fill (overlay->cr);
236   cairo_restore (overlay->cr);
237
238   cairo_save (overlay->cr);
239   cairo_text_extents (overlay->cr, string, &extents);
240   cairo_set_rgb_color (overlay->cr, 1, 1, 1);
241   cairo_set_alpha (overlay->cr, 1.0);
242   switch (overlay->halign) {
243     case GST_TEXT_OVERLAY_HALIGN_LEFT:
244       x = overlay->x0;
245       break;
246     case GST_TEXT_OVERLAY_HALIGN_CENTER:
247       x = overlay->x0 - extents.width / 2;
248       break;
249     case GST_TEXT_OVERLAY_HALIGN_RIGHT:
250       x = overlay->x0 - extents.width;
251       break;
252     default:
253       x = 0;
254   }
255   y = overlay->text_height - 2;
256   cairo_move_to (overlay->cr, x, y);
257   cairo_show_text (overlay->cr, string);
258   cairo_restore (overlay->cr);
259
260   if (overlay->text_outline_image)
261     g_free (overlay->text_outline_image);
262   overlay->text_outline_image =
263       g_malloc (4 * overlay->width * overlay->text_height);
264   cairo_set_target_image (overlay->cr, overlay->text_outline_image,
265       CAIRO_FORMAT_ARGB32, overlay->width, overlay->text_height,
266       overlay->width * 4);
267
268   cairo_save (overlay->cr);
269   cairo_rectangle (overlay->cr, 0, 0, overlay->width, overlay->text_height);
270   cairo_set_rgb_color (overlay->cr, 0, 0, 0);
271   cairo_set_alpha (overlay->cr, 1.0);
272   cairo_set_operator (overlay->cr, CAIRO_OPERATOR_SRC);
273   cairo_fill (overlay->cr);
274   cairo_restore (overlay->cr);
275
276   cairo_save (overlay->cr);
277   cairo_move_to (overlay->cr, x, y);
278   cairo_set_rgb_color (overlay->cr, 1, 1, 1);
279   cairo_set_alpha (overlay->cr, 1.0);
280   cairo_set_line_width (overlay->cr, 1.0);
281   cairo_text_path (overlay->cr, string);
282   cairo_stroke (overlay->cr);
283   cairo_restore (overlay->cr);
284
285   g_free (string);
286 }
287
288 /* static GstPadLinkReturn */
289 /* gst_textoverlay_text_sinkconnect (GstPad *pad, GstCaps *caps) */
290 /* { */
291 /*     return GST_PAD_LINK_DONE; */
292 /* } */
293
294
295 static GstPadLinkReturn
296 gst_textoverlay_video_sinkconnect (GstPad * pad, const GstCaps * caps)
297 {
298   GstTextOverlay *overlay;
299   GstStructure *structure;
300
301   overlay = GST_TEXTOVERLAY (gst_pad_get_parent (pad));
302
303   structure = gst_caps_get_structure (caps, 0);
304   overlay->width = overlay->height = 0;
305   gst_structure_get_int (structure, "width", &overlay->width);
306   gst_structure_get_int (structure, "height", &overlay->height);
307
308   return gst_pad_try_set_caps (overlay->srcpad, caps);
309 }
310
311
312 static void
313 gst_text_overlay_blit_1 (GstTextOverlay * overlay, guchar * dest,
314     guchar * text_image, int val)
315 {
316   int i;
317   int j;
318   int x, a, y;
319   int y0 = 0;
320
321   y = val;
322
323   for (i = 0; i < overlay->text_height; i++) {
324     for (j = 0; j < overlay->width; j++) {
325       x = dest[(i + y0) * overlay->width + j];
326       a = text_image[4 * (i * overlay->width + j) + 1];
327       dest[(i + y0) * overlay->width + j] = (y * a + x * (255 - a)) / 255;
328     }
329   }
330 }
331
332 static void
333 gst_text_overlay_blit_sub2x2 (GstTextOverlay * overlay, guchar * dest,
334     guchar * text_image, int val)
335 {
336   int i;
337   int j;
338   int x, a, y;
339   int y0 = 0;
340
341   y = val;
342
343   for (i = 0; i < overlay->text_height; i += 2) {
344     for (j = 0; j < overlay->width; j += 2) {
345       x = dest[(i / 2 + y0) * (overlay->width / 2) + j / 2];
346       a = (text_image[4 * (i * overlay->width + j) + 1] +
347           text_image[4 * (i * overlay->width + j + 1) + 1] +
348           text_image[4 * ((i + 1) * overlay->width + j) + 1] +
349           text_image[4 * ((i + 1) * overlay->width + j + 1) + 1] + 2) / 4;
350       dest[(i / 2 + y0) * (overlay->width / 2) + j / 2] =
351           (y * a + x * (255 - a)) / 255;
352     }
353   }
354 }
355
356
357 static void
358 gst_textoverlay_video_chain (GstPad * pad, GstData * _data)
359 {
360   GstBuffer *buf = GST_BUFFER (_data);
361   GstTextOverlay *overlay;
362   guchar *pixbuf;
363   gint y;
364
365   g_return_if_fail (pad != NULL);
366   g_return_if_fail (GST_IS_PAD (pad));
367   g_return_if_fail (buf != NULL);
368   overlay = GST_TEXTOVERLAY (gst_pad_get_parent (pad));
369   g_return_if_fail (overlay != NULL);
370   g_return_if_fail (GST_IS_TEXTOVERLAY (overlay));
371
372   if (!GST_IS_BUFFER (_data))
373     return;
374
375   pixbuf = GST_BUFFER_DATA (buf);
376
377   y = overlay->y0;
378   switch (overlay->valign) {
379     case GST_TEXT_OVERLAY_VALIGN_BOTTOM:
380       y -= overlay->text_height;
381       break;
382     case GST_TEXT_OVERLAY_VALIGN_BASELINE:
383 #define BASELINE 2
384       y -= (overlay->text_height - BASELINE);
385       break;
386     case GST_TEXT_OVERLAY_VALIGN_TOP:
387       break;
388   }
389
390   gst_text_overlay_blit_1 (overlay,
391       pixbuf + y * overlay->width, overlay->text_outline_image, 0);
392   gst_text_overlay_blit_sub2x2 (overlay,
393       pixbuf + (overlay->height * overlay->width) +
394       (y / 2) * overlay->width / 2, overlay->text_outline_image, 128);
395   gst_text_overlay_blit_sub2x2 (overlay, pixbuf +
396       (overlay->height * overlay->width) +
397       (overlay->height * overlay->width) / 4 + (y / 2) * overlay->width / 2,
398       overlay->text_outline_image, 128);
399
400   gst_text_overlay_blit_1 (overlay, pixbuf + y * overlay->width,
401       overlay->text_fill_image, 255);
402   gst_text_overlay_blit_sub2x2 (overlay,
403       pixbuf + (overlay->height * overlay->width) +
404       (y / 2) * overlay->width / 2, overlay->text_fill_image, 128);
405   gst_text_overlay_blit_sub2x2 (overlay,
406       pixbuf + (overlay->height * overlay->width) +
407       (overlay->height * overlay->width) / 4 + (y / 2) * overlay->width / 2,
408       overlay->text_fill_image, 128);
409
410   gst_pad_push (overlay->srcpad, GST_DATA (buf));
411 }
412
413 #define PAST_END(buffer, time) \
414   (GST_BUFFER_TIMESTAMP (buffer) != GST_CLOCK_TIME_NONE && \
415    GST_BUFFER_DURATION (buffer) != GST_CLOCK_TIME_NONE && \
416    GST_BUFFER_TIMESTAMP (buffer) + GST_BUFFER_DURATION (buffer) \
417      < (time))
418
419 static void
420 gst_textoverlay_loop (GstElement * element)
421 {
422   GstTextOverlay *overlay;
423   GstBuffer *video_frame;
424   guint64 now;
425
426   g_return_if_fail (element != NULL);
427   g_return_if_fail (GST_IS_TEXTOVERLAY (element));
428   overlay = GST_TEXTOVERLAY (element);
429
430   do {
431     GST_DEBUG ("Attempting to pull next video frame");
432     video_frame = GST_BUFFER (gst_pad_pull (overlay->video_sinkpad));
433     if (GST_IS_EVENT (video_frame)) {
434       GstEvent *event = GST_EVENT (video_frame);
435       GstEventType type = GST_EVENT_TYPE (event);
436
437       gst_pad_event_default (overlay->video_sinkpad, event);
438       GST_DEBUG ("Received event of type %d", type);
439       if (type == GST_EVENT_EOS || type == GST_EVENT_INTERRUPT)
440         return;
441       video_frame = NULL;
442     }
443   } while (!video_frame);
444   now = GST_BUFFER_TIMESTAMP (video_frame);
445   GST_DEBUG ("Got video frame, time=%" GST_TIME_FORMAT, GST_TIME_ARGS (now));
446
447   /*
448    * This state machine has a bug that can't be resolved easily.
449    * (Needs a more complicated state machine.)  Basically, if the
450    * text that came from a buffer from the sink pad is being
451    * displayed, and the default text is changed by set_parameter,
452    * we'll incorrectly display the default text.
453    *
454    * Otherwise, this is a pretty decent state machine that handles
455    * buffer timestamps and durations correctly.  (I think)
456    */
457
458   while ((!overlay->current_buffer ||
459           PAST_END (overlay->current_buffer, now)) &&
460       overlay->next_buffer == NULL) {
461     GST_DEBUG ("attempting to pull a buffer");
462
463     /* read all text buffers until we get one "in the future" */
464     if (!GST_PAD_IS_USABLE (overlay->text_sinkpad)) {
465       break;
466     }
467     do {
468       overlay->next_buffer = GST_BUFFER (gst_pad_pull (overlay->text_sinkpad));
469       if (GST_IS_EVENT (overlay->next_buffer)) {
470         GstEvent *event = GST_EVENT (overlay->next_buffer);
471         GstEventType type = GST_EVENT_TYPE (event);
472
473         gst_pad_event_default (overlay->text_sinkpad, event);
474         if (type == GST_EVENT_EOS || type == GST_EVENT_INTERRUPT)
475           return;
476         overlay->next_buffer = NULL;
477       }
478     } while (!overlay->next_buffer);
479
480     if (PAST_END (overlay->next_buffer, now)) {
481       GST_DEBUG ("Received buffer is past end (%" GST_TIME_FORMAT " + %"
482           GST_TIME_FORMAT " < %" GST_TIME_FORMAT ")",
483           GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (overlay->next_buffer)),
484           GST_TIME_ARGS (GST_BUFFER_DURATION (overlay->next_buffer)),
485           GST_TIME_ARGS (now));
486       gst_buffer_unref (overlay->next_buffer);
487       overlay->next_buffer = NULL;
488     } else {
489       GST_DEBUG ("Received new text buffer of time %" GST_TIME_FORMAT
490           "and duration %" GST_TIME_FORMAT,
491           GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (overlay->next_buffer)),
492           GST_TIME_ARGS (GST_BUFFER_DURATION (overlay->next_buffer)));
493     }
494   }
495
496   if (overlay->next_buffer &&
497       (GST_BUFFER_TIMESTAMP (overlay->next_buffer) <= now ||
498           GST_BUFFER_TIMESTAMP (overlay->next_buffer) == GST_CLOCK_TIME_NONE)) {
499     GST_DEBUG ("using new buffer");
500
501     if (overlay->current_buffer) {
502       gst_buffer_unref (overlay->current_buffer);
503     }
504     overlay->current_buffer = overlay->next_buffer;
505     overlay->next_buffer = NULL;
506
507     GST_DEBUG ("rendering '%*s'",
508         GST_BUFFER_SIZE (overlay->current_buffer),
509         GST_BUFFER_DATA (overlay->current_buffer));
510     gst_textoverlay_render_text (overlay,
511         GST_BUFFER_DATA (overlay->current_buffer),
512         GST_BUFFER_SIZE (overlay->current_buffer));
513     overlay->need_render = FALSE;
514   }
515
516   if (overlay->current_buffer && PAST_END (overlay->current_buffer, now)) {
517     GST_DEBUG ("dropping old buffer");
518
519     gst_buffer_unref (overlay->current_buffer);
520     overlay->current_buffer = NULL;
521
522     overlay->need_render = TRUE;
523   }
524
525   if (overlay->need_render) {
526     GST_DEBUG ("rendering '%s'", overlay->default_text);
527     gst_textoverlay_render_text (overlay,
528         overlay->default_text, strlen (overlay->default_text));
529
530     overlay->need_render = FALSE;
531   }
532
533   gst_textoverlay_video_chain (overlay->srcpad, GST_DATA (video_frame));
534 }
535
536 static void
537 gst_textoverlay_font_init (GstTextOverlay * overlay)
538 {
539   cairo_font_extents_t font_extents;
540
541   cairo_select_font (overlay->cr, overlay->font, overlay->slant,
542       overlay->weight);
543   cairo_scale_font (overlay->cr, overlay->scale);
544
545   cairo_current_font_extents (overlay->cr, &font_extents);
546   overlay->text_height = font_extents.height;
547   if (overlay->text_height & 1)
548     overlay->text_height++;
549
550   overlay->need_render = TRUE;
551 }
552
553 static GstStateChangeReturn
554 gst_textoverlay_change_state (GstElement * element, GstStateChange transition)
555 {
556   GstTextOverlay *overlay;
557
558   overlay = GST_TEXTOVERLAY (element);
559
560   switch (transition) {
561     case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
562       break;
563     case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
564       break;
565     case GST_STATE_CHANGE_PAUSED_TO_READY:
566       break;
567     default:
568       break;
569   }
570
571   parent_class->change_state (element, transition);
572
573   return GST_STATE_CHANGE_SUCCESS;
574 }
575
576 static void
577 gst_textoverlay_finalize (GObject * object)
578 {
579   GstTextOverlay *overlay = GST_TEXTOVERLAY (object);
580
581   if (overlay->cr) {
582     cairo_destroy (overlay->cr);
583   }
584
585   G_OBJECT_CLASS (parent_class)->finalize (object);
586 }
587
588 static void
589 gst_textoverlay_init (GstTextOverlay * overlay)
590 {
591   /* video sink */
592   overlay->video_sinkpad =
593       gst_pad_new_from_template (gst_static_pad_template_get
594       (&video_sink_template_factory), "video_sink");
595   gst_pad_set_link_function (overlay->video_sinkpad,
596       gst_textoverlay_video_sinkconnect);
597   gst_element_add_pad (GST_ELEMENT (overlay), overlay->video_sinkpad);
598
599   /* text sink */
600   overlay->text_sinkpad =
601       gst_pad_new_from_template (gst_static_pad_template_get
602       (&text_sink_template_factory), "text_sink");
603   gst_element_add_pad (GST_ELEMENT (overlay), overlay->text_sinkpad);
604
605   /* (video) source */
606   overlay->srcpad =
607       gst_pad_new_from_template (gst_static_pad_template_get
608       (&textoverlay_src_template_factory), "src");
609   gst_element_add_pad (GST_ELEMENT (overlay), overlay->srcpad);
610
611   overlay->cr = cairo_create ();
612
613   overlay->halign = GST_TEXT_OVERLAY_HALIGN_CENTER;
614   overlay->valign = GST_TEXT_OVERLAY_VALIGN_BASELINE;
615   overlay->x0 = overlay->y0 = 25;
616
617   overlay->default_text = g_strdup ("");
618   overlay->need_render = TRUE;
619
620   overlay->font = g_strdup ("sans");
621   overlay->slant = CAIRO_FONT_SLANT_NORMAL;
622   overlay->weight = CAIRO_FONT_WEIGHT_NORMAL;
623   overlay->scale = 20;
624   gst_textoverlay_font_init (overlay);
625
626   gst_element_set_loop_function (GST_ELEMENT (overlay), gst_textoverlay_loop);
627 }
628
629 static void
630 gst_textoverlay_set_property (GObject * object, guint prop_id,
631     const GValue * value, GParamSpec * pspec)
632 {
633   GstTextOverlay *overlay;
634
635   g_return_if_fail (GST_IS_TEXTOVERLAY (object));
636   overlay = GST_TEXTOVERLAY (object);
637
638   switch (prop_id) {
639
640     case ARG_TEXT:
641       if (overlay->default_text) {
642         g_free (overlay->default_text);
643       }
644       overlay->default_text = g_strdup (g_value_get_string (value));
645       overlay->need_render = TRUE;
646       break;
647
648     case ARG_VALIGN:
649       if (strcasecmp (g_value_get_string (value), "baseline") == 0)
650         overlay->valign = GST_TEXT_OVERLAY_VALIGN_BASELINE;
651       else if (strcasecmp (g_value_get_string (value), "bottom") == 0)
652         overlay->valign = GST_TEXT_OVERLAY_VALIGN_BOTTOM;
653       else if (strcasecmp (g_value_get_string (value), "top") == 0)
654         overlay->valign = GST_TEXT_OVERLAY_VALIGN_TOP;
655       else
656         g_warning ("Invalid 'valign' property value: %s",
657             g_value_get_string (value));
658       overlay->need_render = TRUE;
659       break;
660
661     case ARG_HALIGN:
662       if (strcasecmp (g_value_get_string (value), "left") == 0)
663         overlay->halign = GST_TEXT_OVERLAY_HALIGN_LEFT;
664       else if (strcasecmp (g_value_get_string (value), "right") == 0)
665         overlay->halign = GST_TEXT_OVERLAY_HALIGN_RIGHT;
666       else if (strcasecmp (g_value_get_string (value), "center") == 0)
667         overlay->halign = GST_TEXT_OVERLAY_HALIGN_CENTER;
668       else
669         g_warning ("Invalid 'halign' property value: %s",
670             g_value_get_string (value));
671       overlay->need_render = TRUE;
672       break;
673
674     case ARG_X0:
675       overlay->x0 = g_value_get_int (value);
676       break;
677
678     case ARG_Y0:
679       overlay->y0 = g_value_get_int (value);
680       break;
681
682     case ARG_FONT_DESC:
683       if (overlay->font)
684         g_free (overlay->font);
685       overlay->font = g_strdup (g_value_get_string (value));
686       gst_textoverlay_font_init (overlay);
687       break;
688
689     default:
690       break;
691   }
692 }
693
694 static void
695 gst_textoverlay_get_property (GObject * object, guint prop_id, GValue * value,
696     GParamSpec * pspec)
697 {
698   GstTextOverlay *overlay;
699
700   g_return_if_fail (GST_IS_TEXTOVERLAY (object));
701   overlay = GST_TEXTOVERLAY (object);
702
703   switch (prop_id) {
704     default:
705       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
706       break;
707   }
708 }