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 /* it's not null if we got it, but it might not be ours */
297 g_return_if_fail (GST_IS_ALPHA (object));
299 alpha = GST_ALPHA (object);
303 alpha->method = g_value_get_enum (value);
304 switch (alpha->method) {
305 case ALPHA_METHOD_GREEN:
307 alpha->target_g = 255;
310 case ALPHA_METHOD_BLUE:
313 alpha->target_b = 255;
318 gst_alpha_init_params (alpha);
321 alpha->alpha = g_value_get_double (value);
324 alpha->target_r = g_value_get_uint (value);
325 gst_alpha_init_params (alpha);
328 alpha->target_g = g_value_get_uint (value);
329 gst_alpha_init_params (alpha);
332 alpha->target_b = g_value_get_uint (value);
333 gst_alpha_init_params (alpha);
336 alpha->angle = g_value_get_float (value);
337 gst_alpha_init_params (alpha);
339 case ARG_NOISE_LEVEL:
340 alpha->noise_level = g_value_get_float (value);
341 gst_alpha_init_params (alpha);
344 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
349 gst_alpha_get_property (GObject * object, guint prop_id, GValue * value,
354 /* it's not null if we got it, but it might not be ours */
355 g_return_if_fail (GST_IS_ALPHA (object));
357 alpha = GST_ALPHA (object);
361 g_value_set_enum (value, alpha->method);
364 g_value_set_double (value, alpha->alpha);
367 g_value_set_uint (value, alpha->target_r);
370 g_value_set_uint (value, alpha->target_g);
373 g_value_set_uint (value, alpha->target_b);
376 g_value_set_float (value, alpha->angle);
378 case ARG_NOISE_LEVEL:
379 g_value_set_float (value, alpha->noise_level);
382 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
388 gst_alpha_sink_setcaps (GstPad * pad, GstCaps * caps)
391 GstStructure *structure;
395 alpha = GST_ALPHA (GST_PAD_PARENT (pad));
396 structure = gst_caps_get_structure (caps, 0);
398 if (gst_structure_get_fourcc (structure, "format", &fourcc)) {
400 case GST_MAKE_FOURCC ('I', '4', '2', '0'):
403 case GST_MAKE_FOURCC ('A', 'Y', 'U', 'V'):
413 ret = gst_structure_get_int (structure, "width", &alpha->in_width);
414 ret &= gst_structure_get_int (structure, "height", &alpha->in_height);
420 gst_alpha_set_ayuv (guint8 * src, guint8 * dest, gint width, gint height,
423 gint b_alpha = (gint) (alpha * 255);
429 width = ROUND_UP_2 (width);
430 height = ROUND_UP_2 (height);
432 stride = ROUND_UP_4 (width);
433 size = stride * height;
435 wrap = stride - width;
437 for (i = 0; i < height; i++) {
438 for (j = 0; j < width; j++) {
439 *dest++ = (*src++ * b_alpha) >> 8;
450 gst_alpha_set_i420 (guint8 * src, guint8 * dest, gint width, gint height,
453 gint b_alpha = (gint) (alpha * 255);
459 gint stride, stride2;
462 width = ROUND_UP_2 (width);
463 height = ROUND_UP_2 (height);
465 stride = ROUND_UP_4 (width);
466 size = stride * height;
467 stride2 = ROUND_UP_8 (width) / 2;
468 size2 = stride2 * height / 2;
470 wrap = stride - 2 * (width / 2);
471 wrap2 = stride2 - width / 2;
477 for (i = 0; i < height; i++) {
478 for (j = 0; j < width / 2; j++) {
500 gst_alpha_chroma_key_ayuv (guint8 * src, guint8 * dest, gint width, gint height,
507 gint x, z, u, v, y, a;
514 width = ROUND_UP_2 (width);
515 height = ROUND_UP_2 (height);
517 stride = ROUND_UP_4 (width);
518 size = stride * height;
523 wrap = stride - width;
525 for (i = 0; i < height; i++) {
526 for (j = 0; j < width; j++) {
527 a = *src1++ * (alpha->alpha);
532 /* Convert foreground to XZ coords where X direction is defined by
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);
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
547 tmp = ((short) (x) * alpha->accept_angle_tg) >> 4;
548 tmp = MIN (tmp, 127);
551 /* keep foreground Kfg = 0 */
554 /* Compute Kfg (implicitly) and Kbg, suppress foreground in XZ coord
556 tmp = ((short) (z) * alpha->accept_angle_ctg) >> 4;
557 tmp = CLAMP (tmp, -128, 127);
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;
568 tmp = ((unsigned short) (tmp1) * alpha->kfgy_scale) >> 4;
569 tmp1 = MIN (tmp, 255);
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);
579 tmp = ((char) (x1) * (short) (alpha->cr) +
580 (char) (y1) * (short) (alpha->cb)) >> 7;
581 v = CLAMP (tmp, -128, 127);
583 /* Deal with noise. For now, a circle around the key color with
584 radius of noise_level treated as exact key color. Introduces
587 tmp = z * (short) (z) + (x - alpha->kg) * (short) (x - alpha->kg);
588 tmp = MIN (tmp, 0xffff);
590 if (tmp < alpha->noise_level * alpha->noise_level) {
608 /* based on http://www.cs.utah.edu/~michael/chroma/
611 gst_alpha_chroma_key_i420 (guint8 * src, guint8 * dest, gint width, gint height,
615 guint8 *srcY1, *srcY2, *srcU, *srcV;
616 guint8 *dest1, *dest2;
618 gint x, z, u, v, y11, y12, y21, y22, a;
620 gint stride, stride2;
621 gint wrap, wrap2, wrap3;
625 width = ROUND_UP_2 (width);
626 height = ROUND_UP_2 (height);
628 stride = ROUND_UP_4 (width);
629 size = stride * height;
630 stride2 = ROUND_UP_8 (width) / 2;
631 size2 = stride2 * height / 2;
634 srcY2 = src + stride;
639 dest2 = dest + width * 4;
641 wrap = 2 * stride - 2 * (width / 2);
642 wrap2 = stride2 - width / 2;
643 wrap3 = 8 * width - 8 * (width / 2);
645 a = 255 * alpha->alpha;
647 for (i = 0; i < height / 2; i++) {
648 for (j = 0; j < width / 2; j++) {
656 /* Convert foreground to XZ coords where X direction is defined by
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);
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
671 tmp = ((short) (x) * alpha->accept_angle_tg) >> 4;
672 tmp = MIN (tmp, 127);
675 /* keep foreground Kfg = 0 */
678 /* Compute Kfg (implicitly) and Kbg, suppress foreground in XZ coord
680 tmp = ((short) (z) * alpha->accept_angle_ctg) >> 4;
681 tmp = CLAMP (tmp, -128, 127);
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;
692 tmp = ((unsigned short) (tmp1) * alpha->kfgy_scale) >> 4;
693 tmp1 = MIN (tmp, 255);
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);
709 tmp = ((char) (x1) * (short) (alpha->cr) +
710 (char) (y1) * (short) (alpha->cb)) >> 7;
711 v = CLAMP (tmp, -128, 127);
713 /* Deal with noise. For now, a circle around the key color with
714 radius of noise_level treated as exact key color. Introduces
717 tmp = z * (short) (z) + (x - alpha->kg) * (short) (x - alpha->kg);
718 tmp = MIN (tmp, 0xffff);
720 if (tmp < alpha->noise_level * alpha->noise_level) {
721 /* Uncomment this if you want total suppression within the noise circle */
756 gst_alpha_init_params (GstAlpha * alpha)
763 0.257 * alpha->target_r + 0.504 * alpha->target_g +
764 0.098 * alpha->target_b;
766 -0.148 * alpha->target_r - 0.291 * alpha->target_g +
767 0.439 * alpha->target_b;
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);
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;
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);
792 gst_alpha_chain (GstPad * pad, GstBuffer * buffer)
796 gint new_width, new_height;
799 alpha = GST_ALPHA (GST_PAD_PARENT (pad));
801 new_width = alpha->in_width;
802 new_height = alpha->in_height;
804 if (new_width != alpha->out_width ||
805 new_height != alpha->out_height || !GST_PAD_CAPS (alpha->srcpad)) {
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);
813 gst_pad_set_caps (alpha->srcpad, newcaps);
815 alpha->out_width = new_width;
816 alpha->out_height = new_height;
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);
826 switch (alpha->method) {
827 case ALPHA_METHOD_SET:
829 gst_alpha_set_ayuv (GST_BUFFER_DATA (buffer),
830 GST_BUFFER_DATA (outbuf), new_width, new_height, alpha->alpha);
832 gst_alpha_set_i420 (GST_BUFFER_DATA (buffer),
833 GST_BUFFER_DATA (outbuf), new_width, new_height, alpha->alpha);
836 case ALPHA_METHOD_GREEN:
837 case ALPHA_METHOD_BLUE:
838 case ALPHA_METHOD_CUSTOM:
840 gst_alpha_chroma_key_ayuv (GST_BUFFER_DATA (buffer),
841 GST_BUFFER_DATA (outbuf), new_width, new_height, alpha);
843 gst_alpha_chroma_key_i420 (GST_BUFFER_DATA (buffer),
844 GST_BUFFER_DATA (outbuf), new_width, new_height, alpha);
851 gst_buffer_unref (buffer);
853 ret = gst_pad_push (alpha->srcpad, outbuf);
858 static GstElementStateReturn
859 gst_alpha_change_state (GstElement * element)
863 alpha = GST_ALPHA (element);
865 switch (GST_STATE_TRANSITION (element)) {
866 case GST_STATE_NULL_TO_READY:
868 case GST_STATE_READY_TO_PAUSED:
869 gst_alpha_init_params (alpha);
871 case GST_STATE_PAUSED_TO_PLAYING:
873 case GST_STATE_PLAYING_TO_PAUSED:
875 case GST_STATE_PAUSED_TO_READY:
877 case GST_STATE_READY_TO_NULL:
881 parent_class->change_state (element);
883 return GST_STATE_SUCCESS;
887 plugin_init (GstPlugin * plugin)
889 return gst_element_register (plugin, "alpha", GST_RANK_NONE, GST_TYPE_ALPHA);
892 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
895 "resizes a video by adding borders or cropping",
896 plugin_init, VERSION, GST_LICENSE, GST_PACKAGE, GST_ORIGIN)