4f58a8fb9a8a5c4f94ad74c3ab8b3edf8dd32a9b
[platform/upstream/gstreamer.git] / gst / effectv / gstdice.c
1 /* GStreamer
2  * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
3  *
4  * dice.c: a 'dicing' effect
5  *  copyright (c) 2001 Sam Mertens.  This code is subject to the provisions of
6  *  the GNU Library Public License.
7  *
8  * I suppose this looks similar to PuzzleTV, but it's not. The screen is
9  * divided into small squares, each of which is rotated either 0, 90, 180 or
10  * 270 degrees.  The amount of rotation for each square is chosen at random.
11  */
12
13 #ifdef HAVE_CONFIG_H
14 #include "config.h"
15 #endif
16 #include <string.h>
17 #include <gst/gst.h>
18 #include "gsteffectv.h"
19
20 #define GST_TYPE_DICETV \
21   (gst_dicetv_get_type())
22 #define GST_DICETV(obj) \
23   (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_DICETV,GstDiceTV))
24 #define GST_DICETV_CLASS(klass) \
25   (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_ULAW,GstDiceTV))
26 #define GST_IS_DICETV(obj) \
27   (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_DICETV))
28 #define GST_IS_DICETV_CLASS(obj) \
29   (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_DICETV))
30
31 typedef struct _GstDiceTV GstDiceTV;
32 typedef struct _GstDiceTVClass GstDiceTVClass;
33
34 #define DEFAULT_CUBE_BITS   4
35 #define MAX_CUBE_BITS       5
36 #define MIN_CUBE_BITS       0
37
38 typedef enum _dice_dir 
39 {
40   DICE_UP       = 0,
41   DICE_RIGHT    = 1,
42   DICE_DOWN     = 2,
43   DICE_LEFT     = 3
44 } DiceDir;
45
46 struct _GstDiceTV
47 {
48   GstElement element;
49
50   GstPad *sinkpad, *srcpad;
51
52   gint width, height;
53   gchar* dicemap;
54
55   gint g_cube_bits;
56   gint g_cube_size;
57   gint g_map_height;
58   gint g_map_width;
59 };
60
61 struct _GstDiceTVClass
62 {
63   GstElementClass parent_class;
64
65   void (*reset) (GstElement *element);
66 };
67
68 /* elementfactory information */
69 static GstElementDetails gst_dicetv_details = GST_ELEMENT_DETAILS (
70   "DiceTV",
71   "Filter/Effect/Video",
72   "'Dices' the screen up into many small squares",
73   "Wim Taymans <wim.taymans@chello.be>"
74 );
75
76
77 /* Filter signals and args */
78 enum
79 {
80   /* FILL ME */
81   RESET_SIGNAL,
82   LAST_SIGNAL
83 };
84
85 enum
86 {
87   ARG_0,
88   ARG_CUBE_BITS,
89 };
90
91 static void     gst_dicetv_base_init            (gpointer g_class);   
92 static void     gst_dicetv_class_init           (GstDiceTVClass * klass);
93 static void     gst_dicetv_init                 (GstDiceTV * filter);
94
95 static void     gst_dicetv_reset_handler        (GstElement *elem);
96 static void     gst_dicetv_create_map           (GstDiceTV *filter);
97
98 static void     gst_dicetv_set_property         (GObject * object, guint prop_id,
99                                                  const GValue * value, GParamSpec * pspec);
100 static void     gst_dicetv_get_property         (GObject * object, guint prop_id,
101                                                  GValue * value, GParamSpec * pspec);
102
103 static void     gst_dicetv_chain                (GstPad * pad, GstData *_data);
104
105 static GstElementClass *parent_class = NULL;
106 static guint gst_dicetv_signals[LAST_SIGNAL] = { 0 };
107
108 GType gst_dicetv_get_type (void)
109 {
110   static GType dicetv_type = 0;
111
112   if (!dicetv_type) {
113     static const GTypeInfo dicetv_info = {
114       sizeof (GstDiceTVClass), 
115       gst_dicetv_base_init,
116       NULL,
117       (GClassInitFunc) gst_dicetv_class_init,
118       NULL,
119       NULL,
120       sizeof (GstDiceTV),
121       0,
122       (GInstanceInitFunc) gst_dicetv_init,
123     };
124
125     dicetv_type = g_type_register_static (GST_TYPE_ELEMENT, "GstDiceTV", &dicetv_info, 0);
126   }
127   return dicetv_type;
128 }
129
130 static void
131 gst_dicetv_base_init (gpointer g_class)
132 {
133   GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
134
135   gst_element_class_add_pad_template (element_class, gst_effectv_src_factory ());
136   gst_element_class_add_pad_template (element_class, gst_effectv_sink_factory ());
137  
138   gst_element_class_set_details (element_class, &gst_dicetv_details);
139 }
140
141 static void
142 gst_dicetv_class_init (GstDiceTVClass * klass)
143 {
144   GObjectClass *gobject_class;
145   GstElementClass *gstelement_class;
146
147   gobject_class = (GObjectClass *) klass;
148   gstelement_class = (GstElementClass *) klass;
149
150   parent_class = g_type_class_ref (GST_TYPE_ELEMENT);
151
152   gst_dicetv_signals[RESET_SIGNAL] =
153     g_signal_new ("reset",
154                   G_TYPE_FROM_CLASS (klass),
155                   G_SIGNAL_RUN_LAST,
156                   G_STRUCT_OFFSET (GstDiceTVClass, reset),
157                   NULL, NULL,
158                   g_cclosure_marshal_VOID__VOID,
159                   G_TYPE_NONE, 0);
160
161   klass->reset = gst_dicetv_reset_handler;
162         
163   g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_CUBE_BITS,
164     g_param_spec_int ("square_bits","Square Bits","The size of the Squares",
165                       MIN_CUBE_BITS, MAX_CUBE_BITS, DEFAULT_CUBE_BITS, G_PARAM_READWRITE));
166
167   gobject_class->set_property = gst_dicetv_set_property;
168   gobject_class->get_property = gst_dicetv_get_property;
169 }
170
171 static GstPadLinkReturn
172 gst_dicetv_sinkconnect (GstPad * pad, GstCaps * caps)
173 {
174   GstDiceTV *filter;
175
176   filter = GST_DICETV (gst_pad_get_parent (pad));
177
178   if (!GST_CAPS_IS_FIXED (caps))
179     return GST_PAD_LINK_DELAYED;
180
181   gst_caps_get_int (caps, "width", &filter->width);
182   gst_caps_get_int (caps, "height", &filter->height);
183
184   g_free (filter->dicemap);
185   filter->dicemap = (gchar *) g_malloc (filter->height * filter->width * sizeof(char));
186   gst_dicetv_create_map (filter);
187
188   return gst_pad_try_set_caps (filter->srcpad, gst_caps_ref (caps));
189 }
190
191 static void
192 gst_dicetv_init (GstDiceTV * filter)
193 {
194   filter->sinkpad = gst_pad_new_from_template (gst_effectv_sink_factory (), "sink");
195   gst_pad_set_chain_function (filter->sinkpad, gst_dicetv_chain);
196   gst_pad_set_link_function (filter->sinkpad, gst_dicetv_sinkconnect);
197   gst_element_add_pad (GST_ELEMENT (filter), filter->sinkpad);
198
199   filter->srcpad = gst_pad_new_from_template (gst_effectv_src_factory (), "src");
200   gst_element_add_pad (GST_ELEMENT (filter), filter->srcpad);
201
202   filter->dicemap = NULL;
203   filter->g_cube_bits = DEFAULT_CUBE_BITS;
204   filter->g_cube_size = 0;
205   filter->g_map_height = 0;
206   filter->g_map_width = 0;
207 }
208
209 static void
210 gst_dicetv_reset_handler (GstElement *element)
211 {
212   GstDiceTV *filter = GST_DICETV (element);
213
214   gst_dicetv_create_map (filter);
215 }
216
217
218 static unsigned int
219 fastrand (void)
220 {   
221   static unsigned int fastrand_val;
222
223   return (fastrand_val = fastrand_val * 1103515245 + 12345);
224 }
225
226 static void 
227 gst_dicetv_draw (GstDiceTV *filter, guint32 *src, guint32 *dest)
228 {
229   gint i;
230   gint map_x, map_y, map_i;
231   gint base;
232   gint dx, dy, di;
233   gint video_width = filter->width;
234   gint g_cube_bits = filter->g_cube_bits;
235   gint g_cube_size = filter->g_cube_size;
236     
237   map_i = 0;
238   for (map_y = 0; map_y < filter->g_map_height; map_y++) {
239     for (map_x = 0; map_x < filter->g_map_width; map_x++) {
240        base = (map_y << g_cube_bits) * video_width + (map_x << g_cube_bits);
241
242        switch (filter->dicemap[map_i]) {
243          case DICE_UP:
244            for (dy = 0; dy < g_cube_size; dy++) {
245              i = base + dy * video_width;
246              for (dx = 0; dx < g_cube_size; dx++) {
247                dest[i] = src[i];
248                i++;
249              }
250            }
251            break;
252          case DICE_LEFT:
253            for (dy = 0; dy < g_cube_size; dy++) {
254              i = base + dy * video_width;
255
256              for (dx = 0; dx < g_cube_size; dx++) {
257                di = base + (dx * video_width) + (g_cube_size - dy - 1);
258                dest[di] = src[i];
259                i++;
260              }
261            }
262            break;
263          case DICE_DOWN:
264            for (dy = 0; dy < g_cube_size; dy++) {
265              di = base + dy * video_width;
266              i = base + (g_cube_size - dy - 1) * video_width + g_cube_size;
267              for (dx = 0; dx < g_cube_size; dx++) {
268                i--;
269                dest[di] = src[i];
270                di++;
271              }
272            }
273            break;
274          case DICE_RIGHT:
275            for (dy = 0; dy < g_cube_size; dy++) {
276              i = base + (dy * video_width);
277              for (dx = 0; dx < g_cube_size; dx++) {
278                di = base + dy + (g_cube_size - dx - 1) * video_width;
279                dest[di] = src[i];
280                i++;
281              }
282            }
283            break;
284          default:
285            g_assert_not_reached ();
286            break;
287       }
288       map_i++;
289     }
290   }
291 }
292
293 static void 
294 gst_dicetv_create_map (GstDiceTV *filter)
295 {
296   gint x, y, i;
297     
298   filter->g_map_height = filter->height >> filter->g_cube_bits;
299   filter->g_map_width = filter->width >> filter->g_cube_bits;
300   filter->g_cube_size = 1 << filter->g_cube_bits;
301
302   i = 0;
303
304   for (y = 0; y < filter->g_map_height; y++) {
305     for(x = 0; x < filter->g_map_width; x++) {
306       // dicemap[i] = ((i + y) & 0x3); /* Up, Down, Left or Right */
307       filter->dicemap[i] = (fastrand() >> 24) & 0x03;
308       i++;
309     }
310   }
311 }
312
313 static void
314 gst_dicetv_chain (GstPad * pad, GstData *_data)
315 {
316   GstBuffer *buf = GST_BUFFER (_data);
317   GstDiceTV *filter;
318   guint32 *src, *dest;
319   GstBuffer *outbuf;
320
321   filter = GST_DICETV (gst_pad_get_parent (pad));
322
323   src = (guint32 *) GST_BUFFER_DATA (buf);
324
325   outbuf = gst_buffer_new ();
326   GST_BUFFER_SIZE (outbuf) = (filter->width * filter->height * sizeof(guint32));
327   dest = (guint32 *) GST_BUFFER_DATA (outbuf) = g_malloc (GST_BUFFER_SIZE (outbuf));
328   GST_BUFFER_TIMESTAMP (outbuf) = GST_BUFFER_TIMESTAMP (buf);
329
330   gst_dicetv_draw (filter, src, dest);
331   
332   gst_buffer_unref (buf);
333
334   gst_pad_push (filter->srcpad, GST_DATA (outbuf));
335 }
336
337 static void
338 gst_dicetv_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec)
339 {
340   GstDiceTV *filter;
341
342   /* it's not null if we got it, but it might not be ours */
343   g_return_if_fail (GST_IS_DICETV (object));
344
345   filter = GST_DICETV (object);
346
347   switch (prop_id) {
348     case ARG_CUBE_BITS:
349       filter->g_cube_bits = g_value_get_int (value);
350       gst_dicetv_create_map (filter);
351     default:
352       break;
353   }
354 }
355
356 static void
357 gst_dicetv_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec)
358 {
359   GstDiceTV *filter;
360
361   /* it's not null if we got it, but it might not be ours */
362   g_return_if_fail (GST_IS_DICETV (object));
363
364   filter = GST_DICETV (object);
365
366   switch (prop_id) {
367     case ARG_CUBE_BITS:
368       g_value_set_int (value, filter->g_cube_bits);
369       break;
370     default:
371       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
372       break;
373   }
374 }