2 * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
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.
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.
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.
24 #include <gst/video/video.h>
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))
40 typedef struct _GstAlpha GstAlpha;
41 typedef struct _GstAlphaClass GstAlphaClass;
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)
65 gint in_width, in_height;
66 gint out_width, out_height;
75 GstAlphaMethod method;
80 gfloat y; /* chroma color */
83 gfloat accept_angle_cos;
84 gfloat accept_angle_sin;
85 guint8 accept_angle_tg;
86 guint8 accept_angle_ctg;
93 GstElementClass parent_class;
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>");
104 /* Alpha signals and args */
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
132 static GstStaticPadTemplate gst_alpha_src_template =
133 GST_STATIC_PAD_TEMPLATE ("src",
136 GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("AYUV"))
139 static GstStaticPadTemplate gst_alpha_sink_template =
140 GST_STATIC_PAD_TEMPLATE ("sink",
143 GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("AYUV")
144 ";" GST_VIDEO_CAPS_YUV ("I420")
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);
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);
159 static gboolean gst_alpha_sink_setcaps (GstPad * pad, GstCaps * caps);
160 static GstFlowReturn gst_alpha_chain (GstPad * pad, GstBuffer * buffer);
162 static GstElementStateReturn gst_alpha_change_state (GstElement * element);
165 static GstElementClass *parent_class = NULL;
167 #define GST_TYPE_ALPHA_METHOD (gst_alpha_method_get_type())
169 gst_alpha_method_get_type (void)
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"},
180 if (!alpha_method_type) {
181 alpha_method_type = g_enum_register_static ("GstAlphaMethod", alpha_method);
183 return alpha_method_type;
186 /* static guint gst_alpha_signals[LAST_SIGNAL] = { 0 }; */
189 gst_alpha_get_type (void)
191 static GType alpha_type = 0;
194 static const GTypeInfo alpha_info = {
195 sizeof (GstAlphaClass),
198 (GClassInitFunc) gst_alpha_class_init,
203 (GInstanceInitFunc) gst_alpha_init,
207 g_type_register_static (GST_TYPE_ELEMENT, "GstAlpha", &alpha_info, 0);
213 gst_alpha_base_init (gpointer g_class)
215 GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
217 gst_element_class_set_details (element_class, &gst_alpha_details);
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));
225 gst_alpha_class_init (GstAlphaClass * klass)
227 GObjectClass *gobject_class;
228 GstElementClass *gstelement_class;
230 gobject_class = (GObjectClass *) klass;
231 gstelement_class = (GstElementClass *) klass;
233 parent_class = g_type_class_ref (GST_TYPE_ELEMENT);
235 gobject_class->set_property = gst_alpha_set_property;
236 gobject_class->get_property = gst_alpha_get_property;
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));
261 gstelement_class->change_state = gst_alpha_change_state;
265 gst_alpha_init (GstAlpha * alpha)
267 /* create the sink and src pads */
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);
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);
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;
289 /* do we need this function? */
291 gst_alpha_set_property (GObject * object, guint prop_id,
292 const GValue * value, GParamSpec * pspec)
296 g_return_if_fail (GST_IS_ALPHA (object));
298 alpha = GST_ALPHA (object);
302 alpha->method = g_value_get_enum (value);
303 switch (alpha->method) {
304 case ALPHA_METHOD_GREEN:
306 alpha->target_g = 255;
309 case ALPHA_METHOD_BLUE:
312 alpha->target_b = 255;
317 gst_alpha_init_params (alpha);
320 alpha->alpha = g_value_get_double (value);
323 alpha->target_r = g_value_get_uint (value);
324 gst_alpha_init_params (alpha);
327 alpha->target_g = g_value_get_uint (value);
328 gst_alpha_init_params (alpha);
331 alpha->target_b = g_value_get_uint (value);
332 gst_alpha_init_params (alpha);
335 alpha->angle = g_value_get_float (value);
336 gst_alpha_init_params (alpha);
338 case ARG_NOISE_LEVEL:
339 alpha->noise_level = g_value_get_float (value);
340 gst_alpha_init_params (alpha);
343 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
348 gst_alpha_get_property (GObject * object, guint prop_id, GValue * value,
353 g_return_if_fail (GST_IS_ALPHA (object));
355 alpha = GST_ALPHA (object);
359 g_value_set_enum (value, alpha->method);
362 g_value_set_double (value, alpha->alpha);
365 g_value_set_uint (value, alpha->target_r);
368 g_value_set_uint (value, alpha->target_g);
371 g_value_set_uint (value, alpha->target_b);
374 g_value_set_float (value, alpha->angle);
376 case ARG_NOISE_LEVEL:
377 g_value_set_float (value, alpha->noise_level);
380 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
386 gst_alpha_sink_setcaps (GstPad * pad, GstCaps * caps)
389 GstStructure *structure;
393 alpha = GST_ALPHA (GST_PAD_PARENT (pad));
394 structure = gst_caps_get_structure (caps, 0);
396 if (gst_structure_get_fourcc (structure, "format", &fourcc)) {
398 case GST_MAKE_FOURCC ('I', '4', '2', '0'):
401 case GST_MAKE_FOURCC ('A', 'Y', 'U', 'V'):
411 ret = gst_structure_get_int (structure, "width", &alpha->in_width);
412 ret &= gst_structure_get_int (structure, "height", &alpha->in_height);
418 gst_alpha_set_ayuv (guint8 * src, guint8 * dest, gint width, gint height,
421 gint b_alpha = (gint) (alpha * 255);
427 width = ROUND_UP_2 (width);
428 height = ROUND_UP_2 (height);
430 stride = ROUND_UP_4 (width);
431 size = stride * height;
433 wrap = stride - width;
435 for (i = 0; i < height; i++) {
436 for (j = 0; j < width; j++) {
437 *dest++ = (*src++ * b_alpha) >> 8;
448 gst_alpha_set_i420 (guint8 * src, guint8 * dest, gint width, gint height,
451 gint b_alpha = (gint) (alpha * 255);
457 gint stride, stride2;
460 width = ROUND_UP_2 (width);
461 height = ROUND_UP_2 (height);
463 stride = ROUND_UP_4 (width);
464 size = stride * height;
465 stride2 = ROUND_UP_8 (width) / 2;
466 size2 = stride2 * height / 2;
468 wrap = stride - 2 * (width / 2);
469 wrap2 = stride2 - width / 2;
475 for (i = 0; i < height; i++) {
476 for (j = 0; j < width / 2; j++) {
498 gst_alpha_chroma_key_ayuv (guint8 * src, guint8 * dest, gint width, gint height,
505 gint x, z, u, v, y, a;
512 width = ROUND_UP_2 (width);
513 height = ROUND_UP_2 (height);
515 stride = ROUND_UP_4 (width);
516 size = stride * height;
521 wrap = stride - width;
523 for (i = 0; i < height; i++) {
524 for (j = 0; j < width; j++) {
525 a = *src1++ * (alpha->alpha);
530 /* Convert foreground to XZ coords where X direction is defined by
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);
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
545 tmp = ((short) (x) * alpha->accept_angle_tg) >> 4;
546 tmp = MIN (tmp, 127);
549 /* keep foreground Kfg = 0 */
552 /* Compute Kfg (implicitly) and Kbg, suppress foreground in XZ coord
554 tmp = ((short) (z) * alpha->accept_angle_ctg) >> 4;
555 tmp = CLAMP (tmp, -128, 127);
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;
566 tmp = ((unsigned short) (tmp1) * alpha->kfgy_scale) >> 4;
567 tmp1 = MIN (tmp, 255);
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);
577 tmp = ((char) (x1) * (short) (alpha->cr) +
578 (char) (y1) * (short) (alpha->cb)) >> 7;
579 v = CLAMP (tmp, -128, 127);
581 /* Deal with noise. For now, a circle around the key color with
582 radius of noise_level treated as exact key color. Introduces
585 tmp = z * (short) (z) + (x - alpha->kg) * (short) (x - alpha->kg);
586 tmp = MIN (tmp, 0xffff);
588 if (tmp < alpha->noise_level * alpha->noise_level) {
606 /* based on http://www.cs.utah.edu/~michael/chroma/
609 gst_alpha_chroma_key_i420 (guint8 * src, guint8 * dest, gint width, gint height,
613 guint8 *srcY1, *srcY2, *srcU, *srcV;
614 guint8 *dest1, *dest2;
616 gint x, z, u, v, y11, y12, y21, y22, a;
618 gint stride, stride2;
619 gint wrap, wrap2, wrap3;
623 width = ROUND_UP_2 (width);
624 height = ROUND_UP_2 (height);
626 stride = ROUND_UP_4 (width);
627 size = stride * height;
628 stride2 = ROUND_UP_8 (width) / 2;
629 size2 = stride2 * height / 2;
632 srcY2 = src + stride;
637 dest2 = dest + width * 4;
639 wrap = 2 * stride - 2 * (width / 2);
640 wrap2 = stride2 - width / 2;
641 wrap3 = 8 * width - 8 * (width / 2);
643 a = 255 * alpha->alpha;
645 for (i = 0; i < height / 2; i++) {
646 for (j = 0; j < width / 2; j++) {
654 /* Convert foreground to XZ coords where X direction is defined by
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);
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
669 tmp = ((short) (x) * alpha->accept_angle_tg) >> 4;
670 tmp = MIN (tmp, 127);
673 /* keep foreground Kfg = 0 */
676 /* Compute Kfg (implicitly) and Kbg, suppress foreground in XZ coord
678 tmp = ((short) (z) * alpha->accept_angle_ctg) >> 4;
679 tmp = CLAMP (tmp, -128, 127);
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;
690 tmp = ((unsigned short) (tmp1) * alpha->kfgy_scale) >> 4;
691 tmp1 = MIN (tmp, 255);
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);
707 tmp = ((char) (x1) * (short) (alpha->cr) +
708 (char) (y1) * (short) (alpha->cb)) >> 7;
709 v = CLAMP (tmp, -128, 127);
711 /* Deal with noise. For now, a circle around the key color with
712 radius of noise_level treated as exact key color. Introduces
715 tmp = z * (short) (z) + (x - alpha->kg) * (short) (x - alpha->kg);
716 tmp = MIN (tmp, 0xffff);
718 if (tmp < alpha->noise_level * alpha->noise_level) {
719 /* Uncomment this if you want total suppression within the noise circle */
754 gst_alpha_init_params (GstAlpha * alpha)
761 0.257 * alpha->target_r + 0.504 * alpha->target_g +
762 0.098 * alpha->target_b;
764 -0.148 * alpha->target_r - 0.291 * alpha->target_g +
765 0.439 * alpha->target_b;
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);
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;
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);
790 gst_alpha_chain (GstPad * pad, GstBuffer * buffer)
794 gint new_width, new_height;
797 alpha = GST_ALPHA (GST_PAD_PARENT (pad));
799 new_width = alpha->in_width;
800 new_height = alpha->in_height;
802 if (new_width != alpha->out_width ||
803 new_height != alpha->out_height || !GST_PAD_CAPS (alpha->srcpad)) {
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);
811 gst_pad_set_caps (alpha->srcpad, newcaps);
813 alpha->out_width = new_width;
814 alpha->out_height = new_height;
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);
824 switch (alpha->method) {
825 case ALPHA_METHOD_SET:
827 gst_alpha_set_ayuv (GST_BUFFER_DATA (buffer),
828 GST_BUFFER_DATA (outbuf), new_width, new_height, alpha->alpha);
830 gst_alpha_set_i420 (GST_BUFFER_DATA (buffer),
831 GST_BUFFER_DATA (outbuf), new_width, new_height, alpha->alpha);
834 case ALPHA_METHOD_GREEN:
835 case ALPHA_METHOD_BLUE:
836 case ALPHA_METHOD_CUSTOM:
838 gst_alpha_chroma_key_ayuv (GST_BUFFER_DATA (buffer),
839 GST_BUFFER_DATA (outbuf), new_width, new_height, alpha);
841 gst_alpha_chroma_key_i420 (GST_BUFFER_DATA (buffer),
842 GST_BUFFER_DATA (outbuf), new_width, new_height, alpha);
849 gst_buffer_unref (buffer);
851 ret = gst_pad_push (alpha->srcpad, outbuf);
856 static GstElementStateReturn
857 gst_alpha_change_state (GstElement * element)
861 alpha = GST_ALPHA (element);
863 switch (GST_STATE_TRANSITION (element)) {
864 case GST_STATE_NULL_TO_READY:
866 case GST_STATE_READY_TO_PAUSED:
867 gst_alpha_init_params (alpha);
869 case GST_STATE_PAUSED_TO_PLAYING:
871 case GST_STATE_PLAYING_TO_PAUSED:
873 case GST_STATE_PAUSED_TO_READY:
875 case GST_STATE_READY_TO_NULL:
879 parent_class->change_state (element);
881 return GST_STATE_SUCCESS;
885 plugin_init (GstPlugin * plugin)
887 return gst_element_register (plugin, "alpha", GST_RANK_NONE, GST_TYPE_ALPHA);
890 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
893 "resizes a video by adding borders or cropping",
894 plugin_init, VERSION, GST_LICENSE, GST_PACKAGE, GST_ORIGIN)