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