No need to take the lock anymore, core already did that before calling us.
[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   new_width = alpha->in_width;
802   new_height = alpha->in_height;
803
804   if (new_width != alpha->out_width ||
805       new_height != alpha->out_height || !GST_PAD_CAPS (alpha->srcpad)) {
806     GstCaps *newcaps;
807
808     newcaps = gst_caps_copy (gst_pad_get_negotiated_caps (alpha->sinkpad));
809     gst_caps_set_simple (newcaps,
810         "format", GST_TYPE_FOURCC, GST_STR_FOURCC ("AYUV"),
811         "width", G_TYPE_INT, new_width, "height", G_TYPE_INT, new_height, NULL);
812
813     gst_pad_set_caps (alpha->srcpad, newcaps);
814
815     alpha->out_width = new_width;
816     alpha->out_height = new_height;
817   }
818
819   outbuf =
820       gst_buffer_new_and_alloc (ROUND_UP_2 (new_width) *
821       ROUND_UP_2 (new_height) * 4);
822   gst_buffer_set_caps (outbuf, GST_PAD_CAPS (alpha->srcpad));
823   GST_BUFFER_TIMESTAMP (outbuf) = GST_BUFFER_TIMESTAMP (buffer);
824   GST_BUFFER_DURATION (outbuf) = GST_BUFFER_DURATION (buffer);
825
826   switch (alpha->method) {
827     case ALPHA_METHOD_SET:
828       if (alpha->ayuv) {
829         gst_alpha_set_ayuv (GST_BUFFER_DATA (buffer),
830             GST_BUFFER_DATA (outbuf), new_width, new_height, alpha->alpha);
831       } else {
832         gst_alpha_set_i420 (GST_BUFFER_DATA (buffer),
833             GST_BUFFER_DATA (outbuf), new_width, new_height, alpha->alpha);
834       }
835       break;
836     case ALPHA_METHOD_GREEN:
837     case ALPHA_METHOD_BLUE:
838     case ALPHA_METHOD_CUSTOM:
839       if (alpha->ayuv) {
840         gst_alpha_chroma_key_ayuv (GST_BUFFER_DATA (buffer),
841             GST_BUFFER_DATA (outbuf), new_width, new_height, alpha);
842       } else {
843         gst_alpha_chroma_key_i420 (GST_BUFFER_DATA (buffer),
844             GST_BUFFER_DATA (outbuf), new_width, new_height, alpha);
845       }
846       break;
847     default:
848       break;
849   }
850
851   gst_buffer_unref (buffer);
852
853   ret = gst_pad_push (alpha->srcpad, outbuf);
854
855   return ret;
856 }
857
858 static GstElementStateReturn
859 gst_alpha_change_state (GstElement * element)
860 {
861   GstAlpha *alpha;
862
863   alpha = GST_ALPHA (element);
864
865   switch (GST_STATE_TRANSITION (element)) {
866     case GST_STATE_NULL_TO_READY:
867       break;
868     case GST_STATE_READY_TO_PAUSED:
869       gst_alpha_init_params (alpha);
870       break;
871     case GST_STATE_PAUSED_TO_PLAYING:
872       break;
873     case GST_STATE_PLAYING_TO_PAUSED:
874       break;
875     case GST_STATE_PAUSED_TO_READY:
876       break;
877     case GST_STATE_READY_TO_NULL:
878       break;
879   }
880
881   parent_class->change_state (element);
882
883   return GST_STATE_SUCCESS;
884 }
885
886 static gboolean
887 plugin_init (GstPlugin * plugin)
888 {
889   return gst_element_register (plugin, "alpha", GST_RANK_NONE, GST_TYPE_ALPHA);
890 }
891
892 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
893     GST_VERSION_MINOR,
894     "alpha",
895     "resizes a video by adding borders or cropping",
896     plugin_init, VERSION, GST_LICENSE, GST_PACKAGE, GST_ORIGIN)