Way, way, way too many files: Remove crack comment from the 2000 era.
[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   g_return_if_fail (GST_IS_ALPHA (object));
297
298   alpha = GST_ALPHA (object);
299
300   switch (prop_id) {
301     case ARG_METHOD:
302       alpha->method = g_value_get_enum (value);
303       switch (alpha->method) {
304         case ALPHA_METHOD_GREEN:
305           alpha->target_r = 0;
306           alpha->target_g = 255;
307           alpha->target_b = 0;
308           break;
309         case ALPHA_METHOD_BLUE:
310           alpha->target_r = 0;
311           alpha->target_g = 0;
312           alpha->target_b = 255;
313           break;
314         default:
315           break;
316       }
317       gst_alpha_init_params (alpha);
318       break;
319     case ARG_ALPHA:
320       alpha->alpha = g_value_get_double (value);
321       break;
322     case ARG_TARGET_R:
323       alpha->target_r = g_value_get_uint (value);
324       gst_alpha_init_params (alpha);
325       break;
326     case ARG_TARGET_G:
327       alpha->target_g = g_value_get_uint (value);
328       gst_alpha_init_params (alpha);
329       break;
330     case ARG_TARGET_B:
331       alpha->target_b = g_value_get_uint (value);
332       gst_alpha_init_params (alpha);
333       break;
334     case ARG_ANGLE:
335       alpha->angle = g_value_get_float (value);
336       gst_alpha_init_params (alpha);
337       break;
338     case ARG_NOISE_LEVEL:
339       alpha->noise_level = g_value_get_float (value);
340       gst_alpha_init_params (alpha);
341       break;
342     default:
343       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
344       break;
345   }
346 }
347 static void
348 gst_alpha_get_property (GObject * object, guint prop_id, GValue * value,
349     GParamSpec * pspec)
350 {
351   GstAlpha *alpha;
352
353   g_return_if_fail (GST_IS_ALPHA (object));
354
355   alpha = GST_ALPHA (object);
356
357   switch (prop_id) {
358     case ARG_METHOD:
359       g_value_set_enum (value, alpha->method);
360       break;
361     case ARG_ALPHA:
362       g_value_set_double (value, alpha->alpha);
363       break;
364     case ARG_TARGET_R:
365       g_value_set_uint (value, alpha->target_r);
366       break;
367     case ARG_TARGET_G:
368       g_value_set_uint (value, alpha->target_g);
369       break;
370     case ARG_TARGET_B:
371       g_value_set_uint (value, alpha->target_b);
372       break;
373     case ARG_ANGLE:
374       g_value_set_float (value, alpha->angle);
375       break;
376     case ARG_NOISE_LEVEL:
377       g_value_set_float (value, alpha->noise_level);
378       break;
379     default:
380       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
381       break;
382   }
383 }
384
385 static gboolean
386 gst_alpha_sink_setcaps (GstPad * pad, GstCaps * caps)
387 {
388   GstAlpha *alpha;
389   GstStructure *structure;
390   gboolean ret;
391   guint32 fourcc;
392
393   alpha = GST_ALPHA (GST_PAD_PARENT (pad));
394   structure = gst_caps_get_structure (caps, 0);
395
396   if (gst_structure_get_fourcc (structure, "format", &fourcc)) {
397     switch (fourcc) {
398       case GST_MAKE_FOURCC ('I', '4', '2', '0'):
399         alpha->ayuv = FALSE;
400         break;
401       case GST_MAKE_FOURCC ('A', 'Y', 'U', 'V'):
402         alpha->ayuv = TRUE;
403         break;
404       default:
405         return FALSE;
406     }
407   } else {
408     return FALSE;
409   }
410
411   ret = gst_structure_get_int (structure, "width", &alpha->in_width);
412   ret &= gst_structure_get_int (structure, "height", &alpha->in_height);
413
414   return TRUE;
415 }
416
417 static void
418 gst_alpha_set_ayuv (guint8 * src, guint8 * dest, gint width, gint height,
419     gdouble alpha)
420 {
421   gint b_alpha = (gint) (alpha * 255);
422   gint i, j;
423   gint size;
424   gint stride;
425   gint wrap;
426
427   width = ROUND_UP_2 (width);
428   height = ROUND_UP_2 (height);
429
430   stride = ROUND_UP_4 (width);
431   size = stride * height;
432
433   wrap = stride - width;
434
435   for (i = 0; i < height; i++) {
436     for (j = 0; j < width; j++) {
437       *dest++ = (*src++ * b_alpha) >> 8;
438       *dest++ = *src++;
439       *dest++ = *src++;
440       *dest++ = *src++;
441     }
442     src += wrap;
443     dest += wrap;
444   }
445 }
446
447 static void
448 gst_alpha_set_i420 (guint8 * src, guint8 * dest, gint width, gint height,
449     gdouble alpha)
450 {
451   gint b_alpha = (gint) (alpha * 255);
452   guint8 *srcY;
453   guint8 *srcU;
454   guint8 *srcV;
455   gint i, j;
456   gint size, size2;
457   gint stride, stride2;
458   gint wrap, wrap2;
459
460   width = ROUND_UP_2 (width);
461   height = ROUND_UP_2 (height);
462
463   stride = ROUND_UP_4 (width);
464   size = stride * height;
465   stride2 = ROUND_UP_8 (width) / 2;
466   size2 = stride2 * height / 2;
467
468   wrap = stride - 2 * (width / 2);
469   wrap2 = stride2 - width / 2;
470
471   srcY = src;
472   srcU = srcY + size;
473   srcV = srcU + size2;
474
475   for (i = 0; i < height; i++) {
476     for (j = 0; j < width / 2; j++) {
477       *dest++ = b_alpha;
478       *dest++ = *srcY++;
479       *dest++ = *srcU;
480       *dest++ = *srcV;
481       *dest++ = b_alpha;
482       *dest++ = *srcY++;
483       *dest++ = *srcU++;
484       *dest++ = *srcV++;
485     }
486     if (i % 2 == 0) {
487       srcU -= width / 2;
488       srcV -= width / 2;
489     } else {
490       srcU += wrap2;
491       srcV += wrap2;
492     }
493     srcY += wrap;
494   }
495 }
496
497 static void
498 gst_alpha_chroma_key_ayuv (guint8 * src, guint8 * dest, gint width, gint height,
499     GstAlpha * alpha)
500 {
501   gint b_alpha;
502   guint8 *src1;
503   guint8 *dest1;
504   gint i, j;
505   gint x, z, u, v, y, a;
506   gint size;
507   gint stride;
508   gint wrap;
509   gint tmp, tmp1;
510   gint x1, y1;
511
512   width = ROUND_UP_2 (width);
513   height = ROUND_UP_2 (height);
514
515   stride = ROUND_UP_4 (width);
516   size = stride * height;
517
518   src1 = src;
519   dest1 = dest;
520
521   wrap = stride - width;
522
523   for (i = 0; i < height; i++) {
524     for (j = 0; j < width; j++) {
525       a = *src1++ * (alpha->alpha);
526       y = *src1++;
527       u = *src1++ - 128;
528       v = *src1++ - 128;
529
530       /* Convert foreground to XZ coords where X direction is defined by
531          the key color */
532       tmp = ((short) u * alpha->cb + (short) v * alpha->cr) >> 7;
533       x = CLAMP (tmp, -128, 127);
534       tmp = ((short) v * alpha->cb - (short) u * alpha->cr) >> 7;
535       z = CLAMP (tmp, -128, 127);
536
537       /* WARNING: accept angle should never be set greater than "somewhat less
538          than 90 degrees" to avoid dealing with negative/infinite tg. In reality,
539          80 degrees should be enough if foreground is reasonable. If this seems
540          to be a problem, go to alternative ways of checking point position
541          (scalar product or line equations). This angle should not be too small
542          either to avoid infinite ctg (used to suppress foreground without use of
543          division) */
544
545       tmp = ((short) (x) * alpha->accept_angle_tg) >> 4;
546       tmp = MIN (tmp, 127);
547
548       if (abs (z) > tmp) {
549         /* keep foreground Kfg = 0 */
550         b_alpha = a;
551       } else {
552         /* Compute Kfg (implicitly) and Kbg, suppress foreground in XZ coord
553            according to Kfg */
554         tmp = ((short) (z) * alpha->accept_angle_ctg) >> 4;
555         tmp = CLAMP (tmp, -128, 127);
556         x1 = abs (tmp);
557         y1 = z;
558
559         tmp1 = x - x1;
560         tmp1 = MAX (tmp1, 0);
561         b_alpha = (((unsigned char) (tmp1) *
562                 (unsigned short) (alpha->one_over_kc)) / 2);
563         b_alpha = 255 - CLAMP (b_alpha, 0, 255);
564         b_alpha = (a * b_alpha) >> 8;
565
566         tmp = ((unsigned short) (tmp1) * alpha->kfgy_scale) >> 4;
567         tmp1 = MIN (tmp, 255);
568
569         tmp = y - tmp1;
570         y = MAX (tmp, 0);
571
572         /* Convert suppressed foreground back to CbCr */
573         tmp = ((char) (x1) * (short) (alpha->cb) -
574             (char) (y1) * (short) (alpha->cr)) >> 7;
575         u = CLAMP (tmp, -128, 127);
576
577         tmp = ((char) (x1) * (short) (alpha->cr) +
578             (char) (y1) * (short) (alpha->cb)) >> 7;
579         v = CLAMP (tmp, -128, 127);
580
581         /* Deal with noise. For now, a circle around the key color with
582            radius of noise_level treated as exact key color. Introduces
583            sharp transitions.
584          */
585         tmp = z * (short) (z) + (x - alpha->kg) * (short) (x - alpha->kg);
586         tmp = MIN (tmp, 0xffff);
587
588         if (tmp < alpha->noise_level * alpha->noise_level) {
589           b_alpha = 0;
590         }
591       }
592
593       u += 128;
594       v += 128;
595
596       *dest1++ = b_alpha;
597       *dest1++ = y;
598       *dest1++ = u;
599       *dest1++ = v;
600     }
601     dest1 += wrap;
602     src1 += wrap;
603   }
604 }
605
606 /* based on http://www.cs.utah.edu/~michael/chroma/
607  */
608 static void
609 gst_alpha_chroma_key_i420 (guint8 * src, guint8 * dest, gint width, gint height,
610     GstAlpha * alpha)
611 {
612   gint b_alpha;
613   guint8 *srcY1, *srcY2, *srcU, *srcV;
614   guint8 *dest1, *dest2;
615   gint i, j;
616   gint x, z, u, v, y11, y12, y21, y22, a;
617   gint size, size2;
618   gint stride, stride2;
619   gint wrap, wrap2, wrap3;
620   gint tmp, tmp1;
621   gint x1, y1;
622
623   width = ROUND_UP_2 (width);
624   height = ROUND_UP_2 (height);
625
626   stride = ROUND_UP_4 (width);
627   size = stride * height;
628   stride2 = ROUND_UP_8 (width) / 2;
629   size2 = stride2 * height / 2;
630
631   srcY1 = src;
632   srcY2 = src + stride;
633   srcU = srcY1 + size;
634   srcV = srcU + size2;
635
636   dest1 = dest;
637   dest2 = dest + width * 4;
638
639   wrap = 2 * stride - 2 * (width / 2);
640   wrap2 = stride2 - width / 2;
641   wrap3 = 8 * width - 8 * (width / 2);
642
643   a = 255 * alpha->alpha;
644
645   for (i = 0; i < height / 2; i++) {
646     for (j = 0; j < width / 2; j++) {
647       y11 = *srcY1++;
648       y12 = *srcY1++;
649       y21 = *srcY2++;
650       y22 = *srcY2++;
651       u = *srcU++ - 128;
652       v = *srcV++ - 128;
653
654       /* Convert foreground to XZ coords where X direction is defined by
655          the key color */
656       tmp = ((short) u * alpha->cb + (short) v * alpha->cr) >> 7;
657       x = CLAMP (tmp, -128, 127);
658       tmp = ((short) v * alpha->cb - (short) u * alpha->cr) >> 7;
659       z = CLAMP (tmp, -128, 127);
660
661       /* WARNING: accept angle should never be set greater than "somewhat less
662          than 90 degrees" to avoid dealing with negative/infinite tg. In reality,
663          80 degrees should be enough if foreground is reasonable. If this seems
664          to be a problem, go to alternative ways of checking point position
665          (scalar product or line equations). This angle should not be too small
666          either to avoid infinite ctg (used to suppress foreground without use of
667          division) */
668
669       tmp = ((short) (x) * alpha->accept_angle_tg) >> 4;
670       tmp = MIN (tmp, 127);
671
672       if (abs (z) > tmp) {
673         /* keep foreground Kfg = 0 */
674         b_alpha = 255;
675       } else {
676         /* Compute Kfg (implicitly) and Kbg, suppress foreground in XZ coord
677            according to Kfg */
678         tmp = ((short) (z) * alpha->accept_angle_ctg) >> 4;
679         tmp = CLAMP (tmp, -128, 127);
680         x1 = abs (tmp);
681         y1 = z;
682
683         tmp1 = x - x1;
684         tmp1 = MAX (tmp1, 0);
685         b_alpha = (((unsigned char) (tmp1) *
686                 (unsigned short) (alpha->one_over_kc)) / 2);
687         b_alpha = 255 - CLAMP (b_alpha, 0, 255);
688         b_alpha = (a * b_alpha) >> 8;
689
690         tmp = ((unsigned short) (tmp1) * alpha->kfgy_scale) >> 4;
691         tmp1 = MIN (tmp, 255);
692
693         tmp = y11 - tmp1;
694         y11 = MAX (tmp, 0);
695         tmp = y12 - tmp1;
696         y12 = MAX (tmp, 0);
697         tmp = y21 - tmp1;
698         y21 = MAX (tmp, 0);
699         tmp = y22 - tmp1;
700         y22 = MAX (tmp, 0);
701
702         /* Convert suppressed foreground back to CbCr */
703         tmp = ((char) (x1) * (short) (alpha->cb) -
704             (char) (y1) * (short) (alpha->cr)) >> 7;
705         u = CLAMP (tmp, -128, 127);
706
707         tmp = ((char) (x1) * (short) (alpha->cr) +
708             (char) (y1) * (short) (alpha->cb)) >> 7;
709         v = CLAMP (tmp, -128, 127);
710
711         /* Deal with noise. For now, a circle around the key color with
712            radius of noise_level treated as exact key color. Introduces
713            sharp transitions.
714          */
715         tmp = z * (short) (z) + (x - alpha->kg) * (short) (x - alpha->kg);
716         tmp = MIN (tmp, 0xffff);
717
718         if (tmp < alpha->noise_level * alpha->noise_level) {
719           /* Uncomment this if you want total suppression within the noise circle */
720           b_alpha = 0;
721         }
722       }
723
724       u += 128;
725       v += 128;
726
727       *dest1++ = b_alpha;
728       *dest1++ = y11;
729       *dest1++ = u;
730       *dest1++ = v;
731       *dest1++ = b_alpha;
732       *dest1++ = y12;
733       *dest1++ = u;
734       *dest1++ = v;
735       *dest2++ = b_alpha;
736       *dest2++ = y21;
737       *dest2++ = u;
738       *dest2++ = v;
739       *dest2++ = b_alpha;
740       *dest2++ = y22;
741       *dest2++ = u;
742       *dest2++ = v;
743     }
744     dest1 += wrap3;
745     dest2 += wrap3;
746     srcY1 += wrap;
747     srcY2 += wrap;
748     srcU += wrap2;
749     srcV += wrap2;
750   }
751 }
752
753 static void
754 gst_alpha_init_params (GstAlpha * alpha)
755 {
756   float kgl;
757   float tmp;
758   float tmp1, tmp2;
759
760   alpha->y =
761       0.257 * alpha->target_r + 0.504 * alpha->target_g +
762       0.098 * alpha->target_b;
763   tmp1 =
764       -0.148 * alpha->target_r - 0.291 * alpha->target_g +
765       0.439 * alpha->target_b;
766   tmp2 =
767       0.439 * alpha->target_r - 0.368 * alpha->target_g -
768       0.071 * alpha->target_b;
769   kgl = sqrt (tmp1 * tmp1 + tmp2 * tmp2);
770   alpha->cb = 127 * (tmp1 / kgl);
771   alpha->cr = 127 * (tmp2 / kgl);
772
773   alpha->accept_angle_cos = cos (M_PI * alpha->angle / 180);
774   alpha->accept_angle_sin = sin (M_PI * alpha->angle / 180);
775   tmp = 15 * tan (M_PI * alpha->angle / 180);
776   tmp = MIN (tmp, 255);
777   alpha->accept_angle_tg = tmp;
778   tmp = 15 / tan (M_PI * alpha->angle / 180);
779   tmp = MIN (tmp, 255);
780   alpha->accept_angle_ctg = tmp;
781   tmp = 1 / (kgl);
782   alpha->one_over_kc = 255 * 2 * tmp - 255;
783   tmp = 15 * (float) (alpha->y) / kgl;
784   tmp = MIN (tmp, 255);
785   alpha->kfgy_scale = tmp;
786   alpha->kg = MIN (kgl, 127);
787 }
788
789 static GstFlowReturn
790 gst_alpha_chain (GstPad * pad, GstBuffer * buffer)
791 {
792   GstAlpha *alpha;
793   GstBuffer *outbuf;
794   gint new_width, new_height;
795   GstFlowReturn ret;
796
797   alpha = GST_ALPHA (GST_PAD_PARENT (pad));
798
799   new_width = alpha->in_width;
800   new_height = alpha->in_height;
801
802   if (new_width != alpha->out_width ||
803       new_height != alpha->out_height || !GST_PAD_CAPS (alpha->srcpad)) {
804     GstCaps *newcaps;
805
806     newcaps = gst_caps_copy (gst_pad_get_negotiated_caps (alpha->sinkpad));
807     gst_caps_set_simple (newcaps,
808         "format", GST_TYPE_FOURCC, GST_STR_FOURCC ("AYUV"),
809         "width", G_TYPE_INT, new_width, "height", G_TYPE_INT, new_height, NULL);
810
811     gst_pad_set_caps (alpha->srcpad, newcaps);
812
813     alpha->out_width = new_width;
814     alpha->out_height = new_height;
815   }
816
817   outbuf =
818       gst_buffer_new_and_alloc (ROUND_UP_2 (new_width) *
819       ROUND_UP_2 (new_height) * 4);
820   gst_buffer_set_caps (outbuf, GST_PAD_CAPS (alpha->srcpad));
821   GST_BUFFER_TIMESTAMP (outbuf) = GST_BUFFER_TIMESTAMP (buffer);
822   GST_BUFFER_DURATION (outbuf) = GST_BUFFER_DURATION (buffer);
823
824   switch (alpha->method) {
825     case ALPHA_METHOD_SET:
826       if (alpha->ayuv) {
827         gst_alpha_set_ayuv (GST_BUFFER_DATA (buffer),
828             GST_BUFFER_DATA (outbuf), new_width, new_height, alpha->alpha);
829       } else {
830         gst_alpha_set_i420 (GST_BUFFER_DATA (buffer),
831             GST_BUFFER_DATA (outbuf), new_width, new_height, alpha->alpha);
832       }
833       break;
834     case ALPHA_METHOD_GREEN:
835     case ALPHA_METHOD_BLUE:
836     case ALPHA_METHOD_CUSTOM:
837       if (alpha->ayuv) {
838         gst_alpha_chroma_key_ayuv (GST_BUFFER_DATA (buffer),
839             GST_BUFFER_DATA (outbuf), new_width, new_height, alpha);
840       } else {
841         gst_alpha_chroma_key_i420 (GST_BUFFER_DATA (buffer),
842             GST_BUFFER_DATA (outbuf), new_width, new_height, alpha);
843       }
844       break;
845     default:
846       break;
847   }
848
849   gst_buffer_unref (buffer);
850
851   ret = gst_pad_push (alpha->srcpad, outbuf);
852
853   return ret;
854 }
855
856 static GstElementStateReturn
857 gst_alpha_change_state (GstElement * element)
858 {
859   GstAlpha *alpha;
860
861   alpha = GST_ALPHA (element);
862
863   switch (GST_STATE_TRANSITION (element)) {
864     case GST_STATE_NULL_TO_READY:
865       break;
866     case GST_STATE_READY_TO_PAUSED:
867       gst_alpha_init_params (alpha);
868       break;
869     case GST_STATE_PAUSED_TO_PLAYING:
870       break;
871     case GST_STATE_PLAYING_TO_PAUSED:
872       break;
873     case GST_STATE_PAUSED_TO_READY:
874       break;
875     case GST_STATE_READY_TO_NULL:
876       break;
877   }
878
879   parent_class->change_state (element);
880
881   return GST_STATE_SUCCESS;
882 }
883
884 static gboolean
885 plugin_init (GstPlugin * plugin)
886 {
887   return gst_element_register (plugin, "alpha", GST_RANK_NONE, GST_TYPE_ALPHA);
888 }
889
890 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
891     GST_VERSION_MINOR,
892     "alpha",
893     "resizes a video by adding borders or cropping",
894     plugin_init, VERSION, GST_LICENSE, GST_PACKAGE, GST_ORIGIN)