gst/alpha/gstalpha.c: Fix stride issues. Does not completely work for odd heights.
[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
28 #define GST_TYPE_ALPHA \
29   (gst_alpha_get_type())
30 #define GST_ALPHA(obj) \
31   (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_ALPHA,GstAlpha))
32 #define GST_ALPHA_CLASS(klass) \
33   (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_ALPHA,GstAlphaClass))
34 #define GST_IS_ALPHA(obj) \
35   (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_ALPHA))
36 #define GST_IS_ALPHA_CLASS(obj) \
37   (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_ALPHA))
38
39 typedef struct _GstAlpha GstAlpha;
40 typedef struct _GstAlphaClass GstAlphaClass;
41
42 typedef enum
43 {
44   ALPHA_METHOD_ADD,
45   ALPHA_METHOD_GREEN,
46   ALPHA_METHOD_BLUE,
47   ALPHA_METHOD_BLACK,
48 }
49 GstAlphaMethod;
50
51 #define DEFAULT_METHOD ALPHA_METHOD_ADD
52 #define DEFAULT_ALPHA 1.0
53 #define DEFAULT_TARGET_CR 116
54 #define DEFAULT_TARGET_CB 116
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
68   gdouble alpha;
69
70   guint target_cr, target_cb;
71
72   GstAlphaMethod method;
73 };
74
75 struct _GstAlphaClass
76 {
77   GstElementClass parent_class;
78 };
79
80 /* elementfactory information */
81 static GstElementDetails gst_alpha_details =
82 GST_ELEMENT_DETAILS ("alpha filter",
83     "Filter/Effect/Video",
84     "Adds an alpha channel to video",
85     "Wim Taymans <wim@fluendo.com>");
86
87
88 /* Alpha signals and args */
89 enum
90 {
91   /* FILL ME */
92   LAST_SIGNAL
93 };
94
95 enum
96 {
97   ARG_0,
98   ARG_METHOD,
99   ARG_ALPHA,
100   ARG_TARGET_CR,
101   ARG_TARGET_CB,
102   /* FILL ME */
103 };
104
105 static GstStaticPadTemplate gst_alpha_src_template =
106 GST_STATIC_PAD_TEMPLATE ("src",
107     GST_PAD_SRC,
108     GST_PAD_ALWAYS,
109     GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("AYUV"))
110     );
111
112 static GstStaticPadTemplate gst_alpha_sink_template =
113 GST_STATIC_PAD_TEMPLATE ("sink",
114     GST_PAD_SINK,
115     GST_PAD_ALWAYS,
116     GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("I420"))
117     );
118
119
120 static void gst_alpha_base_init (gpointer g_class);
121 static void gst_alpha_class_init (GstAlphaClass * klass);
122 static void gst_alpha_init (GstAlpha * alpha);
123
124 static void gst_alpha_set_property (GObject * object, guint prop_id,
125     const GValue * value, GParamSpec * pspec);
126 static void gst_alpha_get_property (GObject * object, guint prop_id,
127     GValue * value, GParamSpec * pspec);
128
129 static GstPadLinkReturn
130 gst_alpha_sink_link (GstPad * pad, const GstCaps * caps);
131 static void gst_alpha_chain (GstPad * pad, GstData * _data);
132
133 static GstElementStateReturn gst_alpha_change_state (GstElement * element);
134
135
136 static GstElementClass *parent_class = NULL;
137
138 #define GST_TYPE_ALPHA_METHOD (gst_alpha_method_get_type())
139 static GType
140 gst_alpha_method_get_type (void)
141 {
142   static GType alpha_method_type = 0;
143   static GEnumValue alpha_method[] = {
144     {ALPHA_METHOD_ADD, "0", "Add alpha channel"},
145     {ALPHA_METHOD_GREEN, "1", "Chroma Key green"},
146     {ALPHA_METHOD_BLUE, "2", "Chroma Key blue"},
147     {ALPHA_METHOD_BLACK, "3", "Chroma Key black"},
148     {0, NULL, NULL},
149   };
150
151   if (!alpha_method_type) {
152     alpha_method_type = g_enum_register_static ("GstAlphaMethod", alpha_method);
153   }
154   return alpha_method_type;
155 }
156
157 /* static guint gst_alpha_signals[LAST_SIGNAL] = { 0 }; */
158
159 GType
160 gst_alpha_get_type (void)
161 {
162   static GType alpha_type = 0;
163
164   if (!alpha_type) {
165     static const GTypeInfo alpha_info = {
166       sizeof (GstAlphaClass),
167       gst_alpha_base_init,
168       NULL,
169       (GClassInitFunc) gst_alpha_class_init,
170       NULL,
171       NULL,
172       sizeof (GstAlpha),
173       0,
174       (GInstanceInitFunc) gst_alpha_init,
175     };
176
177     alpha_type =
178         g_type_register_static (GST_TYPE_ELEMENT, "GstAlpha", &alpha_info, 0);
179   }
180   return alpha_type;
181 }
182
183 static void
184 gst_alpha_base_init (gpointer g_class)
185 {
186   GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
187
188   gst_element_class_set_details (element_class, &gst_alpha_details);
189
190   gst_element_class_add_pad_template (element_class,
191       gst_static_pad_template_get (&gst_alpha_sink_template));
192   gst_element_class_add_pad_template (element_class,
193       gst_static_pad_template_get (&gst_alpha_src_template));
194 }
195 static void
196 gst_alpha_class_init (GstAlphaClass * klass)
197 {
198   GObjectClass *gobject_class;
199   GstElementClass *gstelement_class;
200
201   gobject_class = (GObjectClass *) klass;
202   gstelement_class = (GstElementClass *) klass;
203
204   parent_class = g_type_class_ref (GST_TYPE_ELEMENT);
205
206   g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_METHOD,
207       g_param_spec_enum ("method", "Method",
208           "How the alpha channels should be created", GST_TYPE_ALPHA_METHOD,
209           DEFAULT_METHOD, (GParamFlags) G_PARAM_READWRITE));
210   g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_ALPHA,
211       g_param_spec_double ("alpha", "Alpha", "The value for the alpha channel",
212           0.0, 1.0, DEFAULT_ALPHA, (GParamFlags) G_PARAM_READWRITE));
213   g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_TARGET_CR,
214       g_param_spec_uint ("target_cr", "Target Red", "The Red Chroma target", 0,
215           255, 116, (GParamFlags) G_PARAM_READWRITE));
216   g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_TARGET_CB,
217       g_param_spec_uint ("target_cb", "Target Blue", "The Blue Chroma target",
218           0, 255, 116, (GParamFlags) G_PARAM_READWRITE));
219
220   gobject_class->set_property = gst_alpha_set_property;
221   gobject_class->get_property = gst_alpha_get_property;
222
223   gstelement_class->change_state = gst_alpha_change_state;
224 }
225
226 static void
227 gst_alpha_init (GstAlpha * alpha)
228 {
229   /* create the sink and src pads */
230   alpha->sinkpad =
231       gst_pad_new_from_template (gst_static_pad_template_get
232       (&gst_alpha_sink_template), "sink");
233   gst_element_add_pad (GST_ELEMENT (alpha), alpha->sinkpad);
234   gst_pad_set_chain_function (alpha->sinkpad, gst_alpha_chain);
235   gst_pad_set_link_function (alpha->sinkpad, gst_alpha_sink_link);
236
237   alpha->srcpad =
238       gst_pad_new_from_template (gst_static_pad_template_get
239       (&gst_alpha_src_template), "src");
240   gst_element_add_pad (GST_ELEMENT (alpha), alpha->srcpad);
241
242   alpha->alpha = DEFAULT_ALPHA;
243   alpha->method = DEFAULT_METHOD;
244   alpha->target_cr = DEFAULT_TARGET_CR;
245   alpha->target_cb = DEFAULT_TARGET_CB;
246
247   GST_FLAG_SET (alpha, GST_ELEMENT_EVENT_AWARE);
248 }
249
250 /* do we need this function? */
251 static void
252 gst_alpha_set_property (GObject * object, guint prop_id,
253     const GValue * value, GParamSpec * pspec)
254 {
255   GstAlpha *alpha;
256
257   /* it's not null if we got it, but it might not be ours */
258   g_return_if_fail (GST_IS_ALPHA (object));
259
260   alpha = GST_ALPHA (object);
261
262   switch (prop_id) {
263     case ARG_METHOD:
264       alpha->method = g_value_get_enum (value);
265       break;
266     case ARG_ALPHA:
267       alpha->alpha = g_value_get_double (value);
268       break;
269     case ARG_TARGET_CB:
270       alpha->target_cb = g_value_get_uint (value);
271       break;
272     case ARG_TARGET_CR:
273       alpha->target_cr = g_value_get_uint (value);
274       break;
275     default:
276       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
277       break;
278   }
279 }
280 static void
281 gst_alpha_get_property (GObject * object, guint prop_id, GValue * value,
282     GParamSpec * pspec)
283 {
284   GstAlpha *alpha;
285
286   /* it's not null if we got it, but it might not be ours */
287   g_return_if_fail (GST_IS_ALPHA (object));
288
289   alpha = GST_ALPHA (object);
290
291   switch (prop_id) {
292     case ARG_METHOD:
293       g_value_set_enum (value, alpha->method);
294       break;
295     case ARG_ALPHA:
296       g_value_set_double (value, alpha->alpha);
297       break;
298     case ARG_TARGET_CR:
299       g_value_set_uint (value, alpha->target_cr);
300       break;
301     case ARG_TARGET_CB:
302       g_value_set_uint (value, alpha->target_cb);
303       break;
304     default:
305       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
306       break;
307   }
308 }
309
310 static GstPadLinkReturn
311 gst_alpha_sink_link (GstPad * pad, const GstCaps * caps)
312 {
313   GstAlpha *alpha;
314   GstStructure *structure;
315   gboolean ret;
316
317   alpha = GST_ALPHA (gst_pad_get_parent (pad));
318   structure = gst_caps_get_structure (caps, 0);
319
320   ret = gst_structure_get_int (structure, "width", &alpha->in_width);
321   ret &= gst_structure_get_int (structure, "height", &alpha->in_height);
322
323   return GST_PAD_LINK_OK;
324 }
325
326 /*
327 static int yuv_colors_Y[] = {  16, 150,  29 };
328 static int yuv_colors_U[] = { 128,  46, 255 };
329 static int yuv_colors_V[] = { 128,  21, 107 };
330 */
331
332 #define ROUND_UP_4(x) (((x) + 3) & ~3)
333
334 static void
335 gst_alpha_add (guint8 * src, guint8 * dest, gint width, gint height,
336     gdouble alpha)
337 {
338   gint b_alpha = (gint) (alpha * 255);
339   guint8 *srcY;
340   guint8 *srcU;
341   guint8 *srcV;
342   gint i, j;
343   gint w2, h2;
344   gint size, size2;
345   gint stride, stride2;
346   gint wrap, wrap2;
347
348   stride = ROUND_UP_4 (width);
349   size = stride * height;
350   w2 = (width + 1) >> 1;
351   stride2 = ROUND_UP_4 (w2);
352   h2 = (height + 1) >> 1;
353   size2 = stride2 * h2;
354
355   wrap = stride - 2 * (width / 2);
356   wrap2 = stride2 - width / 2;
357
358   srcY = src;
359   srcU = srcY + size;
360   srcV = srcU + size2;
361
362   for (i = 0; i < height; i++) {
363     for (j = 0; j < width / 2; j++) {
364       *dest++ = b_alpha;
365       *dest++ = *srcY++;
366       *dest++ = *srcU;
367       *dest++ = *srcV;
368       *dest++ = b_alpha;
369       *dest++ = *srcY++;
370       *dest++ = *srcU++;
371       *dest++ = *srcV++;
372     }
373     if (i % 2 == 0) {
374       srcU -= width / 2;
375       srcV -= width / 2;
376     } else {
377       srcU += wrap2;
378       srcV += wrap2;
379     }
380     srcY += wrap;
381   }
382 }
383
384 #define ROUND_UP_4(x) (((x) + 3) & ~3)
385 #define ROUND_UP_2(x) (((x) + 1) & ~1)
386
387 static void
388 gst_alpha_chroma_key (gchar * src, gchar * dest, gint width, gint height,
389     gboolean soft, gint target_u, gint target_v, gfloat edge_factor,
390     gdouble alpha)
391 {
392   gint b_alpha;
393   gint f_alpha = (gint) (alpha * 255);
394   guint8 *srcY1, *srcY2, *srcU, *srcV;
395   guint8 *dest1, *dest2;
396   gint i, j;
397   gint x, z, u, v;
398   gint w2, h2;
399   gint size, size2;
400   gint stride, stride2;
401   gint wrap, wrap2, wrap3;
402
403   stride = ROUND_UP_4 (width);
404   size = stride * height;
405   w2 = (width + 1) >> 1;
406   stride2 = ROUND_UP_4 (w2);
407   h2 = (height + 1) >> 1;
408   size2 = stride2 * h2;
409
410   srcY1 = src;
411   srcY2 = src + stride;
412   srcU = srcY1 + size;
413   srcV = srcU + size2;
414
415   dest1 = dest;
416   dest2 = dest + width * 4;
417
418   wrap = 2 * stride - 2 * (width / 2);
419   wrap2 = stride2 - width / 2;
420   wrap3 = 8 * width - 8 * (ROUND_UP_2 (width) / 2);
421
422   for (i = 0; i < height / 2; i++) {
423     for (j = 0; j < width / 2; j++) {
424       u = *srcU++;
425       v = *srcV++;
426
427       x = target_u - u;
428       z = target_v - v;
429
430       // only filter if in top left square
431       if ((x > 0) && (z > 0)) {
432         // only calculate lot of stuff if we'll use soft edges
433         if (soft) {
434           gint ds = (x > z) ? z : x;
435
436           gfloat df = (gfloat) (ds) / edge_factor;
437
438           if (df > 1.0)
439             df = 1.0;
440
441           // suppress foreground
442           if (x > z) {
443             u += z;
444             v += z;
445           } else {
446             u += x;
447             v += x;
448           }
449           b_alpha = (int) (f_alpha * (1.0 - df));
450         } else {
451           // kill color and alpha
452           b_alpha = 0;
453         }
454       } else {
455         // do nothing;
456         b_alpha = f_alpha;
457       }
458
459       *dest1++ = b_alpha;
460       *dest1++ = *srcY1++;
461       *dest1++ = u;
462       *dest1++ = v;
463       *dest1++ = b_alpha;
464       *dest1++ = *srcY1++;
465       *dest1++ = u;
466       *dest1++ = v;
467       *dest2++ = b_alpha;
468       *dest2++ = *srcY2++;
469       *dest2++ = u;
470       *dest2++ = v;
471       *dest2++ = b_alpha;
472       *dest2++ = *srcY2++;
473       *dest2++ = u;
474       *dest2++ = v;
475     }
476     dest1 += wrap3;
477     dest2 += wrap3;
478     srcY1 += wrap;
479     srcY2 += wrap;
480     srcU += wrap2;
481     srcV += wrap2;
482   }
483 }
484
485 static void
486 gst_alpha_chain (GstPad * pad, GstData * _data)
487 {
488   GstBuffer *buffer;
489   GstAlpha *alpha;
490   GstBuffer *outbuf;
491   gint new_width, new_height;
492
493   alpha = GST_ALPHA (gst_pad_get_parent (pad));
494
495   if (GST_IS_EVENT (_data)) {
496     GstEvent *event = GST_EVENT (_data);
497
498     switch (GST_EVENT_TYPE (event)) {
499       default:
500         gst_pad_event_default (pad, event);
501         break;
502     }
503     return;
504   }
505
506   buffer = GST_BUFFER (_data);
507
508   new_width = alpha->in_width;
509   new_height = alpha->in_height;
510
511   if (new_width != alpha->out_width ||
512       new_height != alpha->out_height || !GST_PAD_CAPS (alpha->srcpad)) {
513     GstCaps *newcaps;
514
515     newcaps = gst_caps_copy (gst_pad_get_negotiated_caps (alpha->sinkpad));
516     gst_caps_set_simple (newcaps,
517         "format", GST_TYPE_FOURCC, GST_STR_FOURCC ("AYUV"),
518         "width", G_TYPE_INT, new_width, "height", G_TYPE_INT, new_height, NULL);
519
520     if (!gst_pad_try_set_caps (alpha->srcpad, newcaps)) {
521       GST_ELEMENT_ERROR (alpha, CORE, NEGOTIATION, (NULL), (NULL));
522       return;
523     }
524
525     alpha->out_width = new_width;
526     alpha->out_height = new_height;
527   }
528
529   outbuf = gst_buffer_new_and_alloc (new_width * new_height * 4);
530   GST_BUFFER_TIMESTAMP (outbuf) = GST_BUFFER_TIMESTAMP (buffer);
531   GST_BUFFER_DURATION (outbuf) = GST_BUFFER_DURATION (buffer);
532
533   switch (alpha->method) {
534     case ALPHA_METHOD_ADD:
535       gst_alpha_add (GST_BUFFER_DATA (buffer),
536           GST_BUFFER_DATA (outbuf), new_width, new_height, alpha->alpha);
537       break;
538     case ALPHA_METHOD_GREEN:
539       gst_alpha_chroma_key (GST_BUFFER_DATA (buffer),
540           GST_BUFFER_DATA (outbuf),
541           new_width, new_height,
542           FALSE, alpha->target_cr, alpha->target_cb, 1.0, alpha->alpha);
543       break;
544     case ALPHA_METHOD_BLUE:
545       gst_alpha_chroma_key (GST_BUFFER_DATA (buffer),
546           GST_BUFFER_DATA (outbuf),
547           new_width, new_height, TRUE, 100, 100, 1.0, alpha->alpha);
548       break;
549     case ALPHA_METHOD_BLACK:
550       gst_alpha_chroma_key (GST_BUFFER_DATA (buffer),
551           GST_BUFFER_DATA (outbuf),
552           new_width, new_height, TRUE, 129, 129, 1.0, alpha->alpha);
553       break;
554     default:
555       break;
556   }
557
558   gst_buffer_unref (buffer);
559
560   gst_pad_push (alpha->srcpad, GST_DATA (outbuf));
561 }
562
563 static GstElementStateReturn
564 gst_alpha_change_state (GstElement * element)
565 {
566   GstAlpha *alpha;
567
568   alpha = GST_ALPHA (element);
569
570   switch (GST_STATE_TRANSITION (element)) {
571     case GST_STATE_NULL_TO_READY:
572       break;
573     case GST_STATE_READY_TO_PAUSED:
574       break;
575     case GST_STATE_PAUSED_TO_PLAYING:
576       break;
577     case GST_STATE_PLAYING_TO_PAUSED:
578       break;
579     case GST_STATE_PAUSED_TO_READY:
580       break;
581     case GST_STATE_READY_TO_NULL:
582       break;
583   }
584
585   parent_class->change_state (element);
586
587   return GST_STATE_SUCCESS;
588 }
589
590 static gboolean
591 plugin_init (GstPlugin * plugin)
592 {
593   return gst_element_register (plugin, "alpha", GST_RANK_NONE, GST_TYPE_ALPHA);
594 }
595
596 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
597     GST_VERSION_MINOR,
598     "alpha",
599     "resizes a video by adding borders or cropping",
600     plugin_init, VERSION, GST_LICENSE, GST_PACKAGE, GST_ORIGIN)