Ported alpha, remove alphacolor as functionality is in ffmpegcolorspace.
[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_SET,
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   gboolean ayuv;
68
69   gdouble alpha;
70
71   guint target_r;
72   guint target_g;
73   guint target_b;
74
75   GstAlphaMethod method;
76
77   gfloat angle;
78   gfloat noise_level;
79
80   gfloat y;                     /* chroma color */
81   gint8 cb, cr;
82   gint8 kg;
83   gfloat accept_angle_cos;
84   gfloat accept_angle_sin;
85   guint8 accept_angle_tg;
86   guint8 accept_angle_ctg;
87   guint8 one_over_kc;
88   guint8 kfgy_scale;
89 };
90
91 struct _GstAlphaClass
92 {
93   GstElementClass parent_class;
94 };
95
96 /* elementfactory information */
97 static GstElementDetails gst_alpha_details =
98 GST_ELEMENT_DETAILS ("alpha filter",
99     "Filter/Effect/Video",
100     "Adds an alpha channel to video",
101     "Wim Taymans <wim@fluendo.com>");
102
103
104 /* Alpha signals and args */
105 enum
106 {
107   /* FILL ME */
108   LAST_SIGNAL
109 };
110
111 #define DEFAULT_METHOD ALPHA_METHOD_SET
112 #define DEFAULT_ALPHA 1.0
113 #define DEFAULT_TARGET_R 0
114 #define DEFAULT_TARGET_G 255
115 #define DEFAULT_TARGET_B 0
116 #define DEFAULT_ANGLE 20.0
117 #define DEFAULT_NOISE_LEVEL 2.0
118
119 enum
120 {
121   ARG_0,
122   ARG_METHOD,
123   ARG_ALPHA,
124   ARG_TARGET_R,
125   ARG_TARGET_G,
126   ARG_TARGET_B,
127   ARG_ANGLE,
128   ARG_NOISE_LEVEL,
129   /* FILL ME */
130 };
131
132 static GstStaticPadTemplate gst_alpha_src_template =
133 GST_STATIC_PAD_TEMPLATE ("src",
134     GST_PAD_SRC,
135     GST_PAD_ALWAYS,
136     GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("AYUV"))
137     );
138
139 static GstStaticPadTemplate gst_alpha_sink_template =
140     GST_STATIC_PAD_TEMPLATE ("sink",
141     GST_PAD_SINK,
142     GST_PAD_ALWAYS,
143     GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("AYUV")
144         ";" GST_VIDEO_CAPS_YUV ("I420")
145     )
146     );
147
148
149 static void gst_alpha_base_init (gpointer g_class);
150 static void gst_alpha_class_init (GstAlphaClass * klass);
151 static void gst_alpha_init (GstAlpha * alpha);
152 static void gst_alpha_init_params (GstAlpha * alpha);
153
154 static void gst_alpha_set_property (GObject * object, guint prop_id,
155     const GValue * value, GParamSpec * pspec);
156 static void gst_alpha_get_property (GObject * object, guint prop_id,
157     GValue * value, GParamSpec * pspec);
158
159 static gboolean gst_alpha_sink_setcaps (GstPad * pad, GstCaps * caps);
160 static GstFlowReturn gst_alpha_chain (GstPad * pad, GstBuffer * buffer);
161
162 static GstElementStateReturn gst_alpha_change_state (GstElement * element);
163
164
165 static GstElementClass *parent_class = NULL;
166
167 #define GST_TYPE_ALPHA_METHOD (gst_alpha_method_get_type())
168 static GType
169 gst_alpha_method_get_type (void)
170 {
171   static GType alpha_method_type = 0;
172   static GEnumValue alpha_method[] = {
173     {ALPHA_METHOD_SET, "0", "Set/adjust alpha channel"},
174     {ALPHA_METHOD_GREEN, "1", "Chroma Key green"},
175     {ALPHA_METHOD_BLUE, "2", "Chroma Key blue"},
176     {ALPHA_METHOD_CUSTOM, "3", "Chroma Key on target_r/g/b"},
177     {0, NULL, NULL},
178   };
179
180   if (!alpha_method_type) {
181     alpha_method_type = g_enum_register_static ("GstAlphaMethod", alpha_method);
182   }
183   return alpha_method_type;
184 }
185
186 /* static guint gst_alpha_signals[LAST_SIGNAL] = { 0 }; */
187
188 GType
189 gst_alpha_get_type (void)
190 {
191   static GType alpha_type = 0;
192
193   if (!alpha_type) {
194     static const GTypeInfo alpha_info = {
195       sizeof (GstAlphaClass),
196       gst_alpha_base_init,
197       NULL,
198       (GClassInitFunc) gst_alpha_class_init,
199       NULL,
200       NULL,
201       sizeof (GstAlpha),
202       0,
203       (GInstanceInitFunc) gst_alpha_init,
204     };
205
206     alpha_type =
207         g_type_register_static (GST_TYPE_ELEMENT, "GstAlpha", &alpha_info, 0);
208   }
209   return alpha_type;
210 }
211
212 static void
213 gst_alpha_base_init (gpointer g_class)
214 {
215   GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
216
217   gst_element_class_set_details (element_class, &gst_alpha_details);
218
219   gst_element_class_add_pad_template (element_class,
220       gst_static_pad_template_get (&gst_alpha_sink_template));
221   gst_element_class_add_pad_template (element_class,
222       gst_static_pad_template_get (&gst_alpha_src_template));
223 }
224 static void
225 gst_alpha_class_init (GstAlphaClass * klass)
226 {
227   GObjectClass *gobject_class;
228   GstElementClass *gstelement_class;
229
230   gobject_class = (GObjectClass *) klass;
231   gstelement_class = (GstElementClass *) klass;
232
233   parent_class = g_type_class_ref (GST_TYPE_ELEMENT);
234
235   gobject_class->set_property = gst_alpha_set_property;
236   gobject_class->get_property = gst_alpha_get_property;
237
238   g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_METHOD,
239       g_param_spec_enum ("method", "Method",
240           "How the alpha channels should be created", GST_TYPE_ALPHA_METHOD,
241           DEFAULT_METHOD, (GParamFlags) G_PARAM_READWRITE));
242   g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_ALPHA,
243       g_param_spec_double ("alpha", "Alpha", "The value for the alpha channel",
244           0.0, 1.0, DEFAULT_ALPHA, (GParamFlags) G_PARAM_READWRITE));
245   g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_TARGET_R,
246       g_param_spec_uint ("target_r", "Target Red", "The Red target", 0,
247           255, DEFAULT_TARGET_R, (GParamFlags) G_PARAM_READWRITE));
248   g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_TARGET_G,
249       g_param_spec_uint ("target_g", "Target Green", "The Green target", 0,
250           255, DEFAULT_TARGET_G, (GParamFlags) G_PARAM_READWRITE));
251   g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_TARGET_B,
252       g_param_spec_uint ("target_b", "Target Blue", "The Blue target",
253           0, 255, DEFAULT_TARGET_B, (GParamFlags) G_PARAM_READWRITE));
254   g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_ANGLE,
255       g_param_spec_float ("angle", "Angle", "Size of the colorcube to change",
256           0.0, 90.0, DEFAULT_ANGLE, (GParamFlags) G_PARAM_READWRITE));
257   g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_NOISE_LEVEL,
258       g_param_spec_float ("noise_level", "Noise Level", "Size of noise radius",
259           0.0, 64.0, DEFAULT_NOISE_LEVEL, (GParamFlags) G_PARAM_READWRITE));
260
261   gstelement_class->change_state = gst_alpha_change_state;
262 }
263
264 static void
265 gst_alpha_init (GstAlpha * alpha)
266 {
267   /* create the sink and src pads */
268   alpha->sinkpad =
269       gst_pad_new_from_template (gst_static_pad_template_get
270       (&gst_alpha_sink_template), "sink");
271   gst_element_add_pad (GST_ELEMENT (alpha), alpha->sinkpad);
272   gst_pad_set_chain_function (alpha->sinkpad, gst_alpha_chain);
273   gst_pad_set_setcaps_function (alpha->sinkpad, gst_alpha_sink_setcaps);
274
275   alpha->srcpad =
276       gst_pad_new_from_template (gst_static_pad_template_get
277       (&gst_alpha_src_template), "src");
278   gst_element_add_pad (GST_ELEMENT (alpha), alpha->srcpad);
279
280   alpha->alpha = DEFAULT_ALPHA;
281   alpha->method = DEFAULT_METHOD;
282   alpha->target_r = DEFAULT_TARGET_R;
283   alpha->target_g = DEFAULT_TARGET_G;
284   alpha->target_b = DEFAULT_TARGET_B;
285   alpha->angle = DEFAULT_ANGLE;
286   alpha->noise_level = DEFAULT_NOISE_LEVEL;
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 gboolean
388 gst_alpha_sink_setcaps (GstPad * pad, GstCaps * caps)
389 {
390   GstAlpha *alpha;
391   GstStructure *structure;
392   gboolean ret;
393   guint32 fourcc;
394
395   alpha = GST_ALPHA (GST_PAD_PARENT (pad));
396   structure = gst_caps_get_structure (caps, 0);
397
398   if (gst_structure_get_fourcc (structure, "format", &fourcc)) {
399     switch (fourcc) {
400       case GST_MAKE_FOURCC ('I', '4', '2', '0'):
401         alpha->ayuv = FALSE;
402         break;
403       case GST_MAKE_FOURCC ('A', 'Y', 'U', 'V'):
404         alpha->ayuv = TRUE;
405         break;
406       default:
407         return FALSE;
408     }
409   } else {
410     return FALSE;
411   }
412
413   ret = gst_structure_get_int (structure, "width", &alpha->in_width);
414   ret &= gst_structure_get_int (structure, "height", &alpha->in_height);
415
416   return TRUE;
417 }
418
419 static void
420 gst_alpha_set_ayuv (guint8 * src, guint8 * dest, gint width, gint height,
421     gdouble alpha)
422 {
423   gint b_alpha = (gint) (alpha * 255);
424   gint i, j;
425   gint size;
426   gint stride;
427   gint wrap;
428
429   width = ROUND_UP_2 (width);
430   height = ROUND_UP_2 (height);
431
432   stride = ROUND_UP_4 (width);
433   size = stride * height;
434
435   wrap = stride - width;
436
437   for (i = 0; i < height; i++) {
438     for (j = 0; j < width; j++) {
439       *dest++ = (*src++ * b_alpha) >> 8;
440       *dest++ = *src++;
441       *dest++ = *src++;
442       *dest++ = *src++;
443     }
444     src += wrap;
445     dest += wrap;
446   }
447 }
448
449 static void
450 gst_alpha_set_i420 (guint8 * src, guint8 * dest, gint width, gint height,
451     gdouble alpha)
452 {
453   gint b_alpha = (gint) (alpha * 255);
454   guint8 *srcY;
455   guint8 *srcU;
456   guint8 *srcV;
457   gint i, j;
458   gint size, size2;
459   gint stride, stride2;
460   gint wrap, wrap2;
461
462   width = ROUND_UP_2 (width);
463   height = ROUND_UP_2 (height);
464
465   stride = ROUND_UP_4 (width);
466   size = stride * height;
467   stride2 = ROUND_UP_8 (width) / 2;
468   size2 = stride2 * height / 2;
469
470   wrap = stride - 2 * (width / 2);
471   wrap2 = stride2 - width / 2;
472
473   srcY = src;
474   srcU = srcY + size;
475   srcV = srcU + size2;
476
477   for (i = 0; i < height; i++) {
478     for (j = 0; j < width / 2; j++) {
479       *dest++ = b_alpha;
480       *dest++ = *srcY++;
481       *dest++ = *srcU;
482       *dest++ = *srcV;
483       *dest++ = b_alpha;
484       *dest++ = *srcY++;
485       *dest++ = *srcU++;
486       *dest++ = *srcV++;
487     }
488     if (i % 2 == 0) {
489       srcU -= width / 2;
490       srcV -= width / 2;
491     } else {
492       srcU += wrap2;
493       srcV += wrap2;
494     }
495     srcY += wrap;
496   }
497 }
498
499 static void
500 gst_alpha_chroma_key_ayuv (gchar * src, gchar * dest, gint width, gint height,
501     GstAlpha * alpha)
502 {
503   gint b_alpha;
504   guint8 *src1;
505   guint8 *dest1;
506   gint i, j;
507   gint x, z, u, v, y, a;
508   gint size;
509   gint stride;
510   gint wrap;
511   gint tmp, tmp1;
512   gint x1, y1;
513
514   width = ROUND_UP_2 (width);
515   height = ROUND_UP_2 (height);
516
517   stride = ROUND_UP_4 (width);
518   size = stride * height;
519
520   src1 = src;
521   dest1 = dest;
522
523   wrap = stride - width;
524
525   for (i = 0; i < height; i++) {
526     for (j = 0; j < width; j++) {
527       a = *src1++ * (alpha->alpha);
528       y = *src1++;
529       u = *src1++ - 128;
530       v = *src1++ - 128;
531
532       /* Convert foreground to XZ coords where X direction is defined by
533          the key color */
534       tmp = ((short) u * alpha->cb + (short) v * alpha->cr) >> 7;
535       x = CLAMP (tmp, -128, 127);
536       tmp = ((short) v * alpha->cb - (short) u * alpha->cr) >> 7;
537       z = CLAMP (tmp, -128, 127);
538
539       /* WARNING: accept angle should never be set greater than "somewhat less
540          than 90 degrees" to avoid dealing with negative/infinite tg. In reality,
541          80 degrees should be enough if foreground is reasonable. If this seems
542          to be a problem, go to alternative ways of checking point position
543          (scalar product or line equations). This angle should not be too small
544          either to avoid infinite ctg (used to suppress foreground without use of
545          division) */
546
547       tmp = ((short) (x) * alpha->accept_angle_tg) >> 4;
548       tmp = MIN (tmp, 127);
549
550       if (abs (z) > tmp) {
551         /* keep foreground Kfg = 0 */
552         b_alpha = a;
553       } else {
554         /* Compute Kfg (implicitly) and Kbg, suppress foreground in XZ coord
555            according to Kfg */
556         tmp = ((short) (z) * alpha->accept_angle_ctg) >> 4;
557         tmp = CLAMP (tmp, -128, 127);
558         x1 = abs (tmp);
559         y1 = z;
560
561         tmp1 = x - x1;
562         tmp1 = MAX (tmp1, 0);
563         b_alpha = (((unsigned char) (tmp1) *
564                 (unsigned short) (alpha->one_over_kc)) / 2);
565         b_alpha = 255 - CLAMP (b_alpha, 0, 255);
566         b_alpha = (a * b_alpha) >> 8;
567
568         tmp = ((unsigned short) (tmp1) * alpha->kfgy_scale) >> 4;
569         tmp1 = MIN (tmp, 255);
570
571         tmp = y - tmp1;
572         y = MAX (tmp, 0);
573
574         /* Convert suppressed foreground back to CbCr */
575         tmp = ((char) (x1) * (short) (alpha->cb) -
576             (char) (y1) * (short) (alpha->cr)) >> 7;
577         u = CLAMP (tmp, -128, 127);
578
579         tmp = ((char) (x1) * (short) (alpha->cr) +
580             (char) (y1) * (short) (alpha->cb)) >> 7;
581         v = CLAMP (tmp, -128, 127);
582
583         /* Deal with noise. For now, a circle around the key color with
584            radius of noise_level treated as exact key color. Introduces
585            sharp transitions.
586          */
587         tmp = z * (short) (z) + (x - alpha->kg) * (short) (x - alpha->kg);
588         tmp = MIN (tmp, 0xffff);
589
590         if (tmp < alpha->noise_level * alpha->noise_level) {
591           b_alpha = 0;
592         }
593       }
594
595       u += 128;
596       v += 128;
597
598       *dest1++ = b_alpha;
599       *dest1++ = y;
600       *dest1++ = u;
601       *dest1++ = v;
602     }
603     dest1 += wrap;
604     src1 += wrap;
605   }
606 }
607
608 /* based on http://www.cs.utah.edu/~michael/chroma/
609  */
610 static void
611 gst_alpha_chroma_key_i420 (gchar * src, gchar * dest, gint width, gint height,
612     GstAlpha * alpha)
613 {
614   gint b_alpha;
615   guint8 *srcY1, *srcY2, *srcU, *srcV;
616   guint8 *dest1, *dest2;
617   gint i, j;
618   gint x, z, u, v, y11, y12, y21, y22, a;
619   gint size, size2;
620   gint stride, stride2;
621   gint wrap, wrap2, wrap3;
622   gint tmp, tmp1;
623   gint x1, y1;
624
625   width = ROUND_UP_2 (width);
626   height = ROUND_UP_2 (height);
627
628   stride = ROUND_UP_4 (width);
629   size = stride * height;
630   stride2 = ROUND_UP_8 (width) / 2;
631   size2 = stride2 * height / 2;
632
633   srcY1 = src;
634   srcY2 = src + stride;
635   srcU = srcY1 + size;
636   srcV = srcU + size2;
637
638   dest1 = dest;
639   dest2 = dest + width * 4;
640
641   wrap = 2 * stride - 2 * (width / 2);
642   wrap2 = stride2 - width / 2;
643   wrap3 = 8 * width - 8 * (width / 2);
644
645   a = 255 * alpha->alpha;
646
647   for (i = 0; i < height / 2; i++) {
648     for (j = 0; j < width / 2; j++) {
649       y11 = *srcY1++;
650       y12 = *srcY1++;
651       y21 = *srcY2++;
652       y22 = *srcY2++;
653       u = *srcU++ - 128;
654       v = *srcV++ - 128;
655
656       /* Convert foreground to XZ coords where X direction is defined by
657          the key color */
658       tmp = ((short) u * alpha->cb + (short) v * alpha->cr) >> 7;
659       x = CLAMP (tmp, -128, 127);
660       tmp = ((short) v * alpha->cb - (short) u * alpha->cr) >> 7;
661       z = CLAMP (tmp, -128, 127);
662
663       /* WARNING: accept angle should never be set greater than "somewhat less
664          than 90 degrees" to avoid dealing with negative/infinite tg. In reality,
665          80 degrees should be enough if foreground is reasonable. If this seems
666          to be a problem, go to alternative ways of checking point position
667          (scalar product or line equations). This angle should not be too small
668          either to avoid infinite ctg (used to suppress foreground without use of
669          division) */
670
671       tmp = ((short) (x) * alpha->accept_angle_tg) >> 4;
672       tmp = MIN (tmp, 127);
673
674       if (abs (z) > tmp) {
675         /* keep foreground Kfg = 0 */
676         b_alpha = 255;
677       } else {
678         /* Compute Kfg (implicitly) and Kbg, suppress foreground in XZ coord
679            according to Kfg */
680         tmp = ((short) (z) * alpha->accept_angle_ctg) >> 4;
681         tmp = CLAMP (tmp, -128, 127);
682         x1 = abs (tmp);
683         y1 = z;
684
685         tmp1 = x - x1;
686         tmp1 = MAX (tmp1, 0);
687         b_alpha = (((unsigned char) (tmp1) *
688                 (unsigned short) (alpha->one_over_kc)) / 2);
689         b_alpha = 255 - CLAMP (b_alpha, 0, 255);
690         b_alpha = (a * b_alpha) >> 8;
691
692         tmp = ((unsigned short) (tmp1) * alpha->kfgy_scale) >> 4;
693         tmp1 = MIN (tmp, 255);
694
695         tmp = y11 - tmp1;
696         y11 = MAX (tmp, 0);
697         tmp = y12 - tmp1;
698         y12 = MAX (tmp, 0);
699         tmp = y21 - tmp1;
700         y21 = MAX (tmp, 0);
701         tmp = y22 - tmp1;
702         y22 = MAX (tmp, 0);
703
704         /* Convert suppressed foreground back to CbCr */
705         tmp = ((char) (x1) * (short) (alpha->cb) -
706             (char) (y1) * (short) (alpha->cr)) >> 7;
707         u = CLAMP (tmp, -128, 127);
708
709         tmp = ((char) (x1) * (short) (alpha->cr) +
710             (char) (y1) * (short) (alpha->cb)) >> 7;
711         v = CLAMP (tmp, -128, 127);
712
713         /* Deal with noise. For now, a circle around the key color with
714            radius of noise_level treated as exact key color. Introduces
715            sharp transitions.
716          */
717         tmp = z * (short) (z) + (x - alpha->kg) * (short) (x - alpha->kg);
718         tmp = MIN (tmp, 0xffff);
719
720         if (tmp < alpha->noise_level * alpha->noise_level) {
721           /* Uncomment this if you want total suppression within the noise circle */
722           b_alpha = 0;
723         }
724       }
725
726       u += 128;
727       v += 128;
728
729       *dest1++ = b_alpha;
730       *dest1++ = y11;
731       *dest1++ = u;
732       *dest1++ = v;
733       *dest1++ = b_alpha;
734       *dest1++ = y12;
735       *dest1++ = u;
736       *dest1++ = v;
737       *dest2++ = b_alpha;
738       *dest2++ = y21;
739       *dest2++ = u;
740       *dest2++ = v;
741       *dest2++ = b_alpha;
742       *dest2++ = y22;
743       *dest2++ = u;
744       *dest2++ = v;
745     }
746     dest1 += wrap3;
747     dest2 += wrap3;
748     srcY1 += wrap;
749     srcY2 += wrap;
750     srcU += wrap2;
751     srcV += wrap2;
752   }
753 }
754
755 static void
756 gst_alpha_init_params (GstAlpha * alpha)
757 {
758   float kgl;
759   float tmp;
760   float tmp1, tmp2;
761
762   alpha->y =
763       0.257 * alpha->target_r + 0.504 * alpha->target_g +
764       0.098 * alpha->target_b;
765   tmp1 =
766       -0.148 * alpha->target_r - 0.291 * alpha->target_g +
767       0.439 * alpha->target_b;
768   tmp2 =
769       0.439 * alpha->target_r - 0.368 * alpha->target_g -
770       0.071 * alpha->target_b;
771   kgl = sqrt (tmp1 * tmp1 + tmp2 * tmp2);
772   alpha->cb = 127 * (tmp1 / kgl);
773   alpha->cr = 127 * (tmp2 / kgl);
774
775   alpha->accept_angle_cos = cos (M_PI * alpha->angle / 180);
776   alpha->accept_angle_sin = sin (M_PI * alpha->angle / 180);
777   tmp = 15 * tan (M_PI * alpha->angle / 180);
778   tmp = MIN (tmp, 255);
779   alpha->accept_angle_tg = tmp;
780   tmp = 15 / tan (M_PI * alpha->angle / 180);
781   tmp = MIN (tmp, 255);
782   alpha->accept_angle_ctg = tmp;
783   tmp = 1 / (kgl);
784   alpha->one_over_kc = 255 * 2 * tmp - 255;
785   tmp = 15 * (float) (alpha->y) / kgl;
786   tmp = MIN (tmp, 255);
787   alpha->kfgy_scale = tmp;
788   alpha->kg = MIN (kgl, 127);
789 }
790
791 static GstFlowReturn
792 gst_alpha_chain (GstPad * pad, GstBuffer * buffer)
793 {
794   GstAlpha *alpha;
795   GstBuffer *outbuf;
796   gint new_width, new_height;
797   GstFlowReturn ret;
798
799   alpha = GST_ALPHA (GST_PAD_PARENT (pad));
800
801   GST_STREAM_LOCK (pad);
802
803   new_width = alpha->in_width;
804   new_height = alpha->in_height;
805
806   if (new_width != alpha->out_width ||
807       new_height != alpha->out_height || !GST_RPAD_CAPS (alpha->srcpad)) {
808     GstCaps *newcaps;
809
810     newcaps = gst_caps_copy (gst_pad_get_negotiated_caps (alpha->sinkpad));
811     gst_caps_set_simple (newcaps,
812         "format", GST_TYPE_FOURCC, GST_STR_FOURCC ("AYUV"),
813         "width", G_TYPE_INT, new_width, "height", G_TYPE_INT, new_height, NULL);
814
815     gst_pad_set_caps (alpha->srcpad, newcaps);
816
817     alpha->out_width = new_width;
818     alpha->out_height = new_height;
819   }
820
821   outbuf =
822       gst_buffer_new_and_alloc (ROUND_UP_2 (new_width) *
823       ROUND_UP_2 (new_height) * 4);
824   gst_buffer_set_caps (outbuf, GST_RPAD_CAPS (alpha->srcpad));
825   GST_BUFFER_TIMESTAMP (outbuf) = GST_BUFFER_TIMESTAMP (buffer);
826   GST_BUFFER_DURATION (outbuf) = GST_BUFFER_DURATION (buffer);
827
828   switch (alpha->method) {
829     case ALPHA_METHOD_SET:
830       if (alpha->ayuv) {
831         gst_alpha_set_ayuv (GST_BUFFER_DATA (buffer),
832             GST_BUFFER_DATA (outbuf), new_width, new_height, alpha->alpha);
833       } else {
834         gst_alpha_set_i420 (GST_BUFFER_DATA (buffer),
835             GST_BUFFER_DATA (outbuf), new_width, new_height, alpha->alpha);
836       }
837       break;
838     case ALPHA_METHOD_GREEN:
839     case ALPHA_METHOD_BLUE:
840     case ALPHA_METHOD_CUSTOM:
841       if (alpha->ayuv) {
842         gst_alpha_chroma_key_ayuv (GST_BUFFER_DATA (buffer),
843             GST_BUFFER_DATA (outbuf), new_width, new_height, alpha);
844       } else {
845         gst_alpha_chroma_key_i420 (GST_BUFFER_DATA (buffer),
846             GST_BUFFER_DATA (outbuf), new_width, new_height, alpha);
847       }
848       break;
849     default:
850       break;
851   }
852
853   gst_buffer_unref (buffer);
854
855   ret = gst_pad_push (alpha->srcpad, outbuf);
856
857   GST_STREAM_UNLOCK (pad);
858
859   return ret;
860 }
861
862 static GstElementStateReturn
863 gst_alpha_change_state (GstElement * element)
864 {
865   GstAlpha *alpha;
866
867   alpha = GST_ALPHA (element);
868
869   switch (GST_STATE_TRANSITION (element)) {
870     case GST_STATE_NULL_TO_READY:
871       break;
872     case GST_STATE_READY_TO_PAUSED:
873       gst_alpha_init_params (alpha);
874       break;
875     case GST_STATE_PAUSED_TO_PLAYING:
876       break;
877     case GST_STATE_PLAYING_TO_PAUSED:
878       break;
879     case GST_STATE_PAUSED_TO_READY:
880       break;
881     case GST_STATE_READY_TO_NULL:
882       break;
883   }
884
885   parent_class->change_state (element);
886
887   return GST_STATE_SUCCESS;
888 }
889
890 static gboolean
891 plugin_init (GstPlugin * plugin)
892 {
893   return gst_element_register (plugin, "alpha", GST_RANK_NONE, GST_TYPE_ALPHA);
894 }
895
896 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
897     GST_VERSION_MINOR,
898     "alpha",
899     "resizes a video by adding borders or cropping",
900     plugin_init, VERSION, GST_LICENSE, GST_PACKAGE, GST_ORIGIN)