gst/: More stride fixes.
[platform/upstream/gst-plugins-good.git] / gst / alpha / gstalpha.c
1 /* GStreamer
2  * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19
20 #ifdef HAVE_CONFIG_H
21 #include "config.h"
22 #endif
23 #include <gst/gst.h>
24 #include <gst/video/video.h>
25
26 #include <string.h>
27 #include <math.h>
28
29 #define GST_TYPE_ALPHA \
30   (gst_alpha_get_type())
31 #define GST_ALPHA(obj) \
32   (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_ALPHA,GstAlpha))
33 #define GST_ALPHA_CLASS(klass) \
34   (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_ALPHA,GstAlphaClass))
35 #define GST_IS_ALPHA(obj) \
36   (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_ALPHA))
37 #define GST_IS_ALPHA_CLASS(obj) \
38   (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_ALPHA))
39
40 typedef struct _GstAlpha GstAlpha;
41 typedef struct _GstAlphaClass GstAlphaClass;
42
43 typedef enum
44 {
45   ALPHA_METHOD_ADD,
46   ALPHA_METHOD_GREEN,
47   ALPHA_METHOD_BLUE,
48   ALPHA_METHOD_CUSTOM,
49 }
50 GstAlphaMethod;
51
52 #define ROUND_UP_2(x) (((x) + 1) & ~1)
53 #define ROUND_UP_4(x) (((x) + 3) & ~3)
54 #define ROUND_UP_8(x) (((x) + 7) & ~7)
55
56 struct _GstAlpha
57 {
58   GstElement element;
59
60   /* pads */
61   GstPad *sinkpad;
62   GstPad *srcpad;
63
64   /* caps */
65   gint in_width, in_height;
66   gint out_width, out_height;
67
68   gdouble alpha;
69
70   guint target_r;
71   guint target_g;
72   guint target_b;
73
74   GstAlphaMethod method;
75
76   gfloat angle;
77   gfloat noise_level;
78
79   gfloat y;                     /* chroma color */
80   gint8 cb, cr;
81   gint8 kg;
82   gfloat accept_angle_cos;
83   gfloat accept_angle_sin;
84   guint8 accept_angle_tg;
85   guint8 accept_angle_ctg;
86   guint8 one_over_kc;
87   guint8 kfgy_scale;
88 };
89
90 struct _GstAlphaClass
91 {
92   GstElementClass parent_class;
93 };
94
95 /* elementfactory information */
96 static GstElementDetails gst_alpha_details =
97 GST_ELEMENT_DETAILS ("alpha filter",
98     "Filter/Effect/Video",
99     "Adds an alpha channel to video",
100     "Wim Taymans <wim@fluendo.com>");
101
102
103 /* Alpha signals and args */
104 enum
105 {
106   /* FILL ME */
107   LAST_SIGNAL
108 };
109
110 #define DEFAULT_METHOD ALPHA_METHOD_ADD
111 #define DEFAULT_ALPHA 1.0
112 #define DEFAULT_TARGET_R 0
113 #define DEFAULT_TARGET_G 255
114 #define DEFAULT_TARGET_B 0
115 #define DEFAULT_ANGLE 20.0
116 #define DEFAULT_NOISE_LEVEL 2.0
117
118 enum
119 {
120   ARG_0,
121   ARG_METHOD,
122   ARG_ALPHA,
123   ARG_TARGET_R,
124   ARG_TARGET_G,
125   ARG_TARGET_B,
126   ARG_ANGLE,
127   ARG_NOISE_LEVEL,
128   /* FILL ME */
129 };
130
131 static GstStaticPadTemplate gst_alpha_src_template =
132 GST_STATIC_PAD_TEMPLATE ("src",
133     GST_PAD_SRC,
134     GST_PAD_ALWAYS,
135     GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("AYUV"))
136     );
137
138 static GstStaticPadTemplate gst_alpha_sink_template =
139 GST_STATIC_PAD_TEMPLATE ("sink",
140     GST_PAD_SINK,
141     GST_PAD_ALWAYS,
142     GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("I420"))
143     );
144
145
146 static void gst_alpha_base_init (gpointer g_class);
147 static void gst_alpha_class_init (GstAlphaClass * klass);
148 static void gst_alpha_init (GstAlpha * alpha);
149 static void gst_alpha_init_params (GstAlpha * alpha);
150
151 static void gst_alpha_set_property (GObject * object, guint prop_id,
152     const GValue * value, GParamSpec * pspec);
153 static void gst_alpha_get_property (GObject * object, guint prop_id,
154     GValue * value, GParamSpec * pspec);
155
156 static GstPadLinkReturn
157 gst_alpha_sink_link (GstPad * pad, const GstCaps * caps);
158 static void gst_alpha_chain (GstPad * pad, GstData * _data);
159
160 static GstElementStateReturn gst_alpha_change_state (GstElement * element);
161
162
163 static GstElementClass *parent_class = NULL;
164
165 #define GST_TYPE_ALPHA_METHOD (gst_alpha_method_get_type())
166 static GType
167 gst_alpha_method_get_type (void)
168 {
169   static GType alpha_method_type = 0;
170   static GEnumValue alpha_method[] = {
171     {ALPHA_METHOD_ADD, "0", "Add alpha channel"},
172     {ALPHA_METHOD_GREEN, "1", "Chroma Key green"},
173     {ALPHA_METHOD_BLUE, "2", "Chroma Key blue"},
174     {ALPHA_METHOD_CUSTOM, "3", "Chroma Key on target_r/g/b"},
175     {0, NULL, NULL},
176   };
177
178   if (!alpha_method_type) {
179     alpha_method_type = g_enum_register_static ("GstAlphaMethod", alpha_method);
180   }
181   return alpha_method_type;
182 }
183
184 /* static guint gst_alpha_signals[LAST_SIGNAL] = { 0 }; */
185
186 GType
187 gst_alpha_get_type (void)
188 {
189   static GType alpha_type = 0;
190
191   if (!alpha_type) {
192     static const GTypeInfo alpha_info = {
193       sizeof (GstAlphaClass),
194       gst_alpha_base_init,
195       NULL,
196       (GClassInitFunc) gst_alpha_class_init,
197       NULL,
198       NULL,
199       sizeof (GstAlpha),
200       0,
201       (GInstanceInitFunc) gst_alpha_init,
202     };
203
204     alpha_type =
205         g_type_register_static (GST_TYPE_ELEMENT, "GstAlpha", &alpha_info, 0);
206   }
207   return alpha_type;
208 }
209
210 static void
211 gst_alpha_base_init (gpointer g_class)
212 {
213   GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
214
215   gst_element_class_set_details (element_class, &gst_alpha_details);
216
217   gst_element_class_add_pad_template (element_class,
218       gst_static_pad_template_get (&gst_alpha_sink_template));
219   gst_element_class_add_pad_template (element_class,
220       gst_static_pad_template_get (&gst_alpha_src_template));
221 }
222 static void
223 gst_alpha_class_init (GstAlphaClass * klass)
224 {
225   GObjectClass *gobject_class;
226   GstElementClass *gstelement_class;
227
228   gobject_class = (GObjectClass *) klass;
229   gstelement_class = (GstElementClass *) klass;
230
231   parent_class = g_type_class_ref (GST_TYPE_ELEMENT);
232
233   g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_METHOD,
234       g_param_spec_enum ("method", "Method",
235           "How the alpha channels should be created", GST_TYPE_ALPHA_METHOD,
236           DEFAULT_METHOD, (GParamFlags) G_PARAM_READWRITE));
237   g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_ALPHA,
238       g_param_spec_double ("alpha", "Alpha", "The value for the alpha channel",
239           0.0, 1.0, DEFAULT_ALPHA, (GParamFlags) G_PARAM_READWRITE));
240   g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_TARGET_R,
241       g_param_spec_uint ("target_r", "Target Red", "The Red target", 0,
242           255, DEFAULT_TARGET_R, (GParamFlags) G_PARAM_READWRITE));
243   g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_TARGET_G,
244       g_param_spec_uint ("target_g", "Target Green", "The Green target", 0,
245           255, DEFAULT_TARGET_G, (GParamFlags) G_PARAM_READWRITE));
246   g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_TARGET_B,
247       g_param_spec_uint ("target_b", "Target Blue", "The Blue target",
248           0, 255, DEFAULT_TARGET_B, (GParamFlags) G_PARAM_READWRITE));
249   g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_ANGLE,
250       g_param_spec_float ("angle", "Angle", "Size of the colorcube to change",
251           0.0, 90.0, DEFAULT_ANGLE, (GParamFlags) G_PARAM_READWRITE));
252   g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_NOISE_LEVEL,
253       g_param_spec_float ("noise_level", "Noise Level", "Size of noise radius",
254           0.0, 64.0, DEFAULT_NOISE_LEVEL, (GParamFlags) G_PARAM_READWRITE));
255
256   gobject_class->set_property = gst_alpha_set_property;
257   gobject_class->get_property = gst_alpha_get_property;
258
259   gstelement_class->change_state = gst_alpha_change_state;
260 }
261
262 static void
263 gst_alpha_init (GstAlpha * alpha)
264 {
265   /* create the sink and src pads */
266   alpha->sinkpad =
267       gst_pad_new_from_template (gst_static_pad_template_get
268       (&gst_alpha_sink_template), "sink");
269   gst_element_add_pad (GST_ELEMENT (alpha), alpha->sinkpad);
270   gst_pad_set_chain_function (alpha->sinkpad, gst_alpha_chain);
271   gst_pad_set_link_function (alpha->sinkpad, gst_alpha_sink_link);
272
273   alpha->srcpad =
274       gst_pad_new_from_template (gst_static_pad_template_get
275       (&gst_alpha_src_template), "src");
276   gst_element_add_pad (GST_ELEMENT (alpha), alpha->srcpad);
277
278   alpha->alpha = DEFAULT_ALPHA;
279   alpha->method = DEFAULT_METHOD;
280   alpha->target_r = DEFAULT_TARGET_R;
281   alpha->target_g = DEFAULT_TARGET_G;
282   alpha->target_b = DEFAULT_TARGET_B;
283   alpha->angle = DEFAULT_ANGLE;
284   alpha->noise_level = DEFAULT_NOISE_LEVEL;
285
286   GST_FLAG_SET (alpha, GST_ELEMENT_EVENT_AWARE);
287 }
288
289 /* do we need this function? */
290 static void
291 gst_alpha_set_property (GObject * object, guint prop_id,
292     const GValue * value, GParamSpec * pspec)
293 {
294   GstAlpha *alpha;
295
296   /* it's not null if we got it, but it might not be ours */
297   g_return_if_fail (GST_IS_ALPHA (object));
298
299   alpha = GST_ALPHA (object);
300
301   switch (prop_id) {
302     case ARG_METHOD:
303       alpha->method = g_value_get_enum (value);
304       switch (alpha->method) {
305         case ALPHA_METHOD_GREEN:
306           alpha->target_r = 0;
307           alpha->target_g = 255;
308           alpha->target_b = 0;
309           break;
310         case ALPHA_METHOD_BLUE:
311           alpha->target_r = 0;
312           alpha->target_g = 0;
313           alpha->target_b = 255;
314           break;
315         default:
316           break;
317       }
318       gst_alpha_init_params (alpha);
319       break;
320     case ARG_ALPHA:
321       alpha->alpha = g_value_get_double (value);
322       break;
323     case ARG_TARGET_R:
324       alpha->target_r = g_value_get_uint (value);
325       gst_alpha_init_params (alpha);
326       break;
327     case ARG_TARGET_G:
328       alpha->target_g = g_value_get_uint (value);
329       gst_alpha_init_params (alpha);
330       break;
331     case ARG_TARGET_B:
332       alpha->target_b = g_value_get_uint (value);
333       gst_alpha_init_params (alpha);
334       break;
335     case ARG_ANGLE:
336       alpha->angle = g_value_get_float (value);
337       gst_alpha_init_params (alpha);
338       break;
339     case ARG_NOISE_LEVEL:
340       alpha->noise_level = g_value_get_float (value);
341       gst_alpha_init_params (alpha);
342       break;
343     default:
344       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
345       break;
346   }
347 }
348 static void
349 gst_alpha_get_property (GObject * object, guint prop_id, GValue * value,
350     GParamSpec * pspec)
351 {
352   GstAlpha *alpha;
353
354   /* it's not null if we got it, but it might not be ours */
355   g_return_if_fail (GST_IS_ALPHA (object));
356
357   alpha = GST_ALPHA (object);
358
359   switch (prop_id) {
360     case ARG_METHOD:
361       g_value_set_enum (value, alpha->method);
362       break;
363     case ARG_ALPHA:
364       g_value_set_double (value, alpha->alpha);
365       break;
366     case ARG_TARGET_R:
367       g_value_set_uint (value, alpha->target_r);
368       break;
369     case ARG_TARGET_G:
370       g_value_set_uint (value, alpha->target_g);
371       break;
372     case ARG_TARGET_B:
373       g_value_set_uint (value, alpha->target_b);
374       break;
375     case ARG_ANGLE:
376       g_value_set_float (value, alpha->angle);
377       break;
378     case ARG_NOISE_LEVEL:
379       g_value_set_float (value, alpha->noise_level);
380       break;
381     default:
382       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
383       break;
384   }
385 }
386
387 static GstPadLinkReturn
388 gst_alpha_sink_link (GstPad * pad, const GstCaps * caps)
389 {
390   GstAlpha *alpha;
391   GstStructure *structure;
392   gboolean ret;
393
394   alpha = GST_ALPHA (gst_pad_get_parent (pad));
395   structure = gst_caps_get_structure (caps, 0);
396
397   ret = gst_structure_get_int (structure, "width", &alpha->in_width);
398   ret &= gst_structure_get_int (structure, "height", &alpha->in_height);
399
400   return GST_PAD_LINK_OK;
401 }
402
403 static void
404 gst_alpha_add (guint8 * src, guint8 * dest, gint width, gint height,
405     gdouble alpha)
406 {
407   gint b_alpha = (gint) (alpha * 255);
408   guint8 *srcY;
409   guint8 *srcU;
410   guint8 *srcV;
411   gint i, j;
412   gint size, size2;
413   gint stride, stride2;
414   gint wrap, wrap2;
415
416   width = ROUND_UP_2 (width);
417   height = ROUND_UP_2 (height);
418
419   stride = ROUND_UP_4 (width);
420   size = stride * height;
421   stride2 = ROUND_UP_8 (width) / 2;
422   size2 = stride2 * height / 2;
423
424   wrap = stride - 2 * (width / 2);
425   wrap2 = stride2 - width / 2;
426
427   srcY = src;
428   srcU = srcY + size;
429   srcV = srcU + size2;
430
431   for (i = 0; i < height; i++) {
432     for (j = 0; j < width / 2; j++) {
433       *dest++ = b_alpha;
434       *dest++ = *srcY++;
435       *dest++ = *srcU;
436       *dest++ = *srcV;
437       *dest++ = b_alpha;
438       *dest++ = *srcY++;
439       *dest++ = *srcU++;
440       *dest++ = *srcV++;
441     }
442     if (i % 2 == 0) {
443       srcU -= width / 2;
444       srcV -= width / 2;
445     } else {
446       srcU += wrap2;
447       srcV += wrap2;
448     }
449     srcY += wrap;
450   }
451 }
452
453
454 /* based on http://www.cs.utah.edu/~michael/chroma/
455  */
456 static void
457 gst_alpha_chroma_key (gchar * src, gchar * dest, gint width, gint height,
458     GstAlpha * alpha)
459 {
460   gint b_alpha;
461   guint8 *srcY1, *srcY2, *srcU, *srcV;
462   guint8 *dest1, *dest2;
463   gint i, j;
464   gint x, z, u, v, y11, y12, y21, y22;
465   gint size, size2;
466   gint stride, stride2;
467   gint wrap, wrap2, wrap3;
468   gint tmp, tmp1;
469   gint x1, y1;
470
471   width = ROUND_UP_2 (width);
472   height = ROUND_UP_2 (height);
473
474   stride = ROUND_UP_4 (width);
475   size = stride * height;
476   stride2 = ROUND_UP_8 (width) / 2;
477   size2 = stride2 * height / 2;
478
479   srcY1 = src;
480   srcY2 = src + stride;
481   srcU = srcY1 + size;
482   srcV = srcU + size2;
483
484   dest1 = dest;
485   dest2 = dest + width * 4;
486
487   wrap = 2 * stride - 2 * (width / 2);
488   wrap2 = stride2 - width / 2;
489   wrap3 = 8 * width - 8 * (width / 2);
490
491   for (i = 0; i < height / 2; i++) {
492     for (j = 0; j < width / 2; j++) {
493       y11 = *srcY1++;
494       y12 = *srcY1++;
495       y21 = *srcY2++;
496       y22 = *srcY2++;
497       u = *srcU++ - 128;
498       v = *srcV++ - 128;
499
500       /* Convert foreground to XZ coords where X direction is defined by
501          the key color */
502       tmp = ((short) u * alpha->cb + (short) v * alpha->cr) >> 7;
503       x = CLAMP (tmp, -128, 127);
504       tmp = ((short) v * alpha->cb - (short) u * alpha->cr) >> 7;
505       z = CLAMP (tmp, -128, 127);
506
507       /* WARNING: accept angle should never be set greater than "somewhat less
508          than 90 degrees" to avoid dealing with negative/infinite tg. In reality,
509          80 degrees should be enough if foreground is reasonable. If this seems
510          to be a problem, go to alternative ways of checking point position
511          (scalar product or line equations). This angle should not be too small
512          either to avoid infinite ctg (used to suppress foreground without use of
513          division) */
514
515       tmp = ((short) (x) * alpha->accept_angle_tg) >> 4;
516       tmp = MIN (tmp, 127);
517
518       if (abs (z) > tmp) {
519         /* keep foreground Kfg = 0 */
520         b_alpha = 255;
521       } else {
522         /* Compute Kfg (implicitly) and Kbg, suppress foreground in XZ coord
523            according to Kfg */
524         tmp = ((short) (z) * alpha->accept_angle_ctg) >> 4;
525         tmp = CLAMP (tmp, -128, 127);
526         x1 = abs (tmp);
527         y1 = z;
528
529         tmp1 = x - x1;
530         tmp1 = MAX (tmp1, 0);
531         b_alpha = (((unsigned char) (tmp1) *
532                 (unsigned short) (alpha->one_over_kc)) / 2);
533         b_alpha = 255 - CLAMP (b_alpha, 0, 255);
534
535         tmp = ((unsigned short) (tmp1) * alpha->kfgy_scale) >> 4;
536         tmp1 = MIN (tmp, 255);
537
538         tmp = y11 - tmp1;
539         y11 = MAX (tmp, 0);
540         tmp = y12 - tmp1;
541         y12 = MAX (tmp, 0);
542         tmp = y21 - tmp1;
543         y21 = MAX (tmp, 0);
544         tmp = y22 - tmp1;
545         y22 = MAX (tmp, 0);
546
547         /* Convert suppressed foreground back to CbCr */
548         tmp = ((char) (x1) * (short) (alpha->cb) -
549             (char) (y1) * (short) (alpha->cr)) >> 7;
550         u = CLAMP (tmp, -128, 127);
551
552         tmp = ((char) (x1) * (short) (alpha->cr) +
553             (char) (y1) * (short) (alpha->cb)) >> 7;
554         v = CLAMP (tmp, -128, 127);
555
556         /* Deal with noise. For now, a circle around the key color with
557            radius of noise_level treated as exact key color. Introduces
558            sharp transitions.
559          */
560         tmp = z * (short) (z) + (x - alpha->kg) * (short) (x - alpha->kg);
561         tmp = MIN (tmp, 0xffff);
562
563         if (tmp < alpha->noise_level * alpha->noise_level) {
564           /* Uncomment this if you want total suppression within the noise circle */
565           b_alpha = 0;
566         }
567       }
568
569       u += 128;
570       v += 128;
571
572       *dest1++ = b_alpha;
573       *dest1++ = y11;
574       *dest1++ = u;
575       *dest1++ = v;
576       *dest1++ = b_alpha;
577       *dest1++ = y12;
578       *dest1++ = u;
579       *dest1++ = v;
580       *dest2++ = b_alpha;
581       *dest2++ = y21;
582       *dest2++ = u;
583       *dest2++ = v;
584       *dest2++ = b_alpha;
585       *dest2++ = y22;
586       *dest2++ = u;
587       *dest2++ = v;
588     }
589     dest1 += wrap3;
590     dest2 += wrap3;
591     srcY1 += wrap;
592     srcY2 += wrap;
593     srcU += wrap2;
594     srcV += wrap2;
595   }
596 }
597
598 static void
599 gst_alpha_init_params (GstAlpha * alpha)
600 {
601   float kgl;
602   float tmp;
603   float tmp1, tmp2;
604
605   alpha->y =
606       0.257 * alpha->target_r + 0.504 * alpha->target_g +
607       0.098 * alpha->target_b;
608   tmp1 =
609       -0.148 * alpha->target_r - 0.291 * alpha->target_g +
610       0.439 * alpha->target_b;
611   tmp2 =
612       0.439 * alpha->target_r - 0.368 * alpha->target_g -
613       0.071 * alpha->target_b;
614   kgl = sqrt (tmp1 * tmp1 + tmp2 * tmp2);
615   alpha->cb = 127 * (tmp1 / kgl);
616   alpha->cr = 127 * (tmp2 / kgl);
617
618   alpha->accept_angle_cos = cos (M_PI * alpha->angle / 180);
619   alpha->accept_angle_sin = sin (M_PI * alpha->angle / 180);
620   tmp = 15 * tan (M_PI * alpha->angle / 180);
621   tmp = MIN (tmp, 255);
622   alpha->accept_angle_tg = tmp;
623   tmp = 15 / tan (M_PI * alpha->angle / 180);
624   tmp = MIN (tmp, 255);
625   alpha->accept_angle_ctg = tmp;
626   tmp = 1 / (kgl);
627   alpha->one_over_kc = 255 * 2 * tmp - 255;
628   tmp = 15 * (float) (alpha->y) / kgl;
629   tmp = MIN (tmp, 255);
630   alpha->kfgy_scale = tmp;
631   alpha->kg = MIN (kgl, 127);
632 }
633
634 static void
635 gst_alpha_chain (GstPad * pad, GstData * _data)
636 {
637   GstBuffer *buffer;
638   GstAlpha *alpha;
639   GstBuffer *outbuf;
640   gint new_width, new_height;
641
642   alpha = GST_ALPHA (gst_pad_get_parent (pad));
643
644   if (GST_IS_EVENT (_data)) {
645     GstEvent *event = GST_EVENT (_data);
646
647     switch (GST_EVENT_TYPE (event)) {
648       default:
649         gst_pad_event_default (pad, event);
650         break;
651     }
652     return;
653   }
654
655   buffer = GST_BUFFER (_data);
656
657   new_width = alpha->in_width;
658   new_height = alpha->in_height;
659
660   if (new_width != alpha->out_width ||
661       new_height != alpha->out_height || !GST_PAD_CAPS (alpha->srcpad)) {
662     GstCaps *newcaps;
663
664     newcaps = gst_caps_copy (gst_pad_get_negotiated_caps (alpha->sinkpad));
665     gst_caps_set_simple (newcaps,
666         "format", GST_TYPE_FOURCC, GST_STR_FOURCC ("AYUV"),
667         "width", G_TYPE_INT, new_width, "height", G_TYPE_INT, new_height, NULL);
668
669     if (!gst_pad_try_set_caps (alpha->srcpad, newcaps)) {
670       GST_ELEMENT_ERROR (alpha, CORE, NEGOTIATION, (NULL), (NULL));
671       return;
672     }
673
674     alpha->out_width = new_width;
675     alpha->out_height = new_height;
676   }
677
678   outbuf =
679       gst_buffer_new_and_alloc (ROUND_UP_2 (new_width) *
680       ROUND_UP_2 (new_height) * 4);
681   GST_BUFFER_TIMESTAMP (outbuf) = GST_BUFFER_TIMESTAMP (buffer);
682   GST_BUFFER_DURATION (outbuf) = GST_BUFFER_DURATION (buffer);
683
684   switch (alpha->method) {
685     case ALPHA_METHOD_ADD:
686       gst_alpha_add (GST_BUFFER_DATA (buffer),
687           GST_BUFFER_DATA (outbuf), new_width, new_height, alpha->alpha);
688       break;
689     case ALPHA_METHOD_GREEN:
690       gst_alpha_chroma_key (GST_BUFFER_DATA (buffer),
691           GST_BUFFER_DATA (outbuf), new_width, new_height, alpha);
692       break;
693     case ALPHA_METHOD_BLUE:
694       gst_alpha_chroma_key (GST_BUFFER_DATA (buffer),
695           GST_BUFFER_DATA (outbuf), new_width, new_height, alpha);
696       break;
697     case ALPHA_METHOD_CUSTOM:
698       gst_alpha_chroma_key (GST_BUFFER_DATA (buffer),
699           GST_BUFFER_DATA (outbuf), new_width, new_height, alpha);
700       break;
701     default:
702       break;
703   }
704
705   gst_buffer_unref (buffer);
706
707   gst_pad_push (alpha->srcpad, GST_DATA (outbuf));
708 }
709
710 static GstElementStateReturn
711 gst_alpha_change_state (GstElement * element)
712 {
713   GstAlpha *alpha;
714
715   alpha = GST_ALPHA (element);
716
717   switch (GST_STATE_TRANSITION (element)) {
718     case GST_STATE_NULL_TO_READY:
719       break;
720     case GST_STATE_READY_TO_PAUSED:
721       gst_alpha_init_params (alpha);
722       break;
723     case GST_STATE_PAUSED_TO_PLAYING:
724       break;
725     case GST_STATE_PLAYING_TO_PAUSED:
726       break;
727     case GST_STATE_PAUSED_TO_READY:
728       break;
729     case GST_STATE_READY_TO_NULL:
730       break;
731   }
732
733   parent_class->change_state (element);
734
735   return GST_STATE_SUCCESS;
736 }
737
738 static gboolean
739 plugin_init (GstPlugin * plugin)
740 {
741   return gst_element_register (plugin, "alpha", GST_RANK_NONE, GST_TYPE_ALPHA);
742 }
743
744 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
745     GST_VERSION_MINOR,
746     "alpha",
747     "resizes a video by adding borders or cropping",
748     plugin_init, VERSION, GST_LICENSE, GST_PACKAGE, GST_ORIGIN)