1507
|
1 /* Paranormal - A highly customizable audio visualization library
|
|
2 * Copyright (C) 2001 Jamie Gennis <jgennis@mindspring.com>
|
|
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 Free
|
|
16 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
17 */
|
|
18
|
|
19 #include <math.h>
|
|
20 #include <glib.h>
|
|
21 #include "pndisplacement.h"
|
|
22 #include "pnbooleanoption.h"
|
|
23 #include "pnstringoption.h"
|
|
24 #include "pncpu.h"
|
|
25
|
|
26 enum
|
|
27 {
|
|
28 PN_PIXEL_DISPLACEMENT_NO_PIXEL = 0xffffffff
|
|
29 };
|
|
30
|
|
31 static void pn_displacement_class_init (PnDisplacementClass *class);
|
|
32 static void pn_displacement_init (PnDisplacement *displacement,
|
|
33 PnDisplacementClass *class);
|
|
34 /* PnObject signals */
|
|
35 static void pn_displacement_destroy (PnObject *object);
|
|
36
|
|
37 /* PnActuator methods */
|
|
38 static void pn_displacement_prepare (PnDisplacement *displacement,
|
|
39 PnImage *image);
|
|
40 static void pn_displacement_execute (PnDisplacement *displacement,
|
|
41 PnImage *image,
|
|
42 PnAudioData *audio_data);
|
|
43
|
|
44 static PnActuatorClass *parent_class = NULL;
|
|
45
|
|
46 GType
|
|
47 pn_displacement_get_type (void)
|
|
48 {
|
|
49 static GType displacement_type = 0;
|
|
50
|
|
51 if (! displacement_type)
|
|
52 {
|
|
53 static const GTypeInfo displacement_info =
|
|
54 {
|
|
55 sizeof (PnDisplacementClass),
|
|
56 NULL, /* base_init */
|
|
57 NULL, /* base_finalize */
|
|
58 (GClassInitFunc) pn_displacement_class_init,
|
|
59 NULL, /* class_finalize */
|
|
60 NULL, /* class_data */
|
|
61 sizeof (PnDisplacement),
|
|
62 0, /* n_preallocs */
|
|
63 (GInstanceInitFunc) pn_displacement_init
|
|
64 };
|
|
65
|
|
66 /* FIXME: should this be dynamic? */
|
|
67 displacement_type = g_type_register_static (PN_TYPE_ACTUATOR,
|
|
68 "PnDisplacement",
|
|
69 &displacement_info,
|
|
70 0);
|
|
71 }
|
|
72 return displacement_type;
|
|
73 }
|
|
74
|
|
75 static void
|
|
76 pn_displacement_class_init (PnDisplacementClass *class)
|
|
77 {
|
|
78 GObjectClass *gobject_class;
|
|
79 PnObjectClass *object_class;
|
|
80 PnUserObjectClass *user_object_class;
|
|
81 PnActuatorClass *actuator_class;
|
|
82
|
|
83 parent_class = g_type_class_peek_parent (class);
|
|
84
|
|
85 gobject_class = (GObjectClass *) class;
|
|
86 object_class = (PnObjectClass *) class;
|
|
87 user_object_class = (PnUserObjectClass *) class;
|
|
88 actuator_class = (PnActuatorClass *) class;
|
|
89
|
|
90 /* PnObject signals */
|
|
91 object_class->destroy = pn_displacement_destroy;
|
|
92
|
|
93 /* PnActuator methods */
|
|
94 actuator_class->prepare = (PnActuatorPrepFunc) pn_displacement_prepare;
|
|
95 actuator_class->execute = (PnActuatorExecFunc) pn_displacement_execute;
|
|
96 }
|
|
97
|
|
98 static void
|
|
99 pn_displacement_init (PnDisplacement *displacement, PnDisplacementClass *class)
|
|
100 {
|
|
101 PnStringOption *init_script_opt, *frame_script_opt;
|
|
102
|
|
103 /* Set up the name and description */
|
|
104 pn_user_object_set_name (PN_USER_OBJECT (displacement), "Transform.Displacement");
|
|
105 pn_user_object_set_description (PN_USER_OBJECT (displacement),
|
|
106 "Displaces the image");
|
|
107
|
|
108 /* Set up the options */
|
|
109 init_script_opt = pn_string_option_new ("init_script", "The initialization script");
|
|
110 frame_script_opt = pn_string_option_new ("frame_script", "The per-frame script");
|
|
111
|
|
112 pn_actuator_add_option (PN_ACTUATOR (displacement), PN_OPTION (init_script_opt));
|
|
113 pn_actuator_add_option (PN_ACTUATOR (displacement), PN_OPTION (frame_script_opt));
|
|
114
|
|
115 /* Create the script objects and symbol table */
|
|
116 displacement->init_script = pn_script_new ();
|
|
117 pn_object_ref (PN_OBJECT (displacement->init_script));
|
|
118 pn_object_sink (PN_OBJECT (displacement->init_script));
|
|
119 displacement->frame_script = pn_script_new ();
|
|
120 pn_object_ref (PN_OBJECT (displacement->frame_script));
|
|
121 pn_object_sink (PN_OBJECT (displacement->frame_script));
|
|
122 displacement->symbol_table = pn_symbol_table_new ();
|
|
123 pn_object_ref (PN_OBJECT (displacement->symbol_table));
|
|
124 pn_object_sink (PN_OBJECT (displacement->symbol_table));
|
|
125
|
|
126 /* Get the variables from the symbol table */
|
|
127 displacement->x_var = pn_symbol_table_ref_variable_by_name (displacement->symbol_table, "x");
|
|
128 displacement->y_var = pn_symbol_table_ref_variable_by_name (displacement->symbol_table, "y");
|
|
129 displacement->volume_var = pn_symbol_table_ref_variable_by_name (displacement->symbol_table, "volume");
|
|
130 }
|
|
131
|
|
132 static void
|
|
133 pn_displacement_destroy (PnObject *object)
|
|
134 {
|
|
135 PnDisplacement *displacement;
|
|
136
|
|
137 displacement = (PnDisplacement *) object;
|
|
138
|
|
139 pn_object_unref (PN_OBJECT (displacement->init_script));
|
|
140 pn_object_unref (PN_OBJECT (displacement->frame_script));
|
|
141 pn_object_unref (PN_OBJECT (displacement->symbol_table));
|
|
142 }
|
|
143
|
|
144 /* FIXME: optimize this */
|
|
145 static void
|
|
146 pn_displacement_prepare (PnDisplacement *displacement, PnImage *image)
|
|
147 {
|
|
148 PnStringOption *init_script_opt, *frame_script_opt;
|
|
149
|
|
150 g_return_if_fail (displacement != NULL);
|
|
151 g_return_if_fail (PN_IS_DISPLACEMENT (displacement));
|
|
152 g_return_if_fail (image != NULL);
|
|
153 g_return_if_fail (PN_IS_IMAGE (image));
|
|
154
|
|
155 /* Parse the script strings */
|
|
156 init_script_opt =
|
|
157 (PnStringOption *) pn_actuator_get_option_by_index (PN_ACTUATOR (displacement),
|
|
158 PN_DISPLACEMENT_OPT_INIT_SCRIPT);
|
|
159 frame_script_opt =
|
|
160 (PnStringOption *) pn_actuator_get_option_by_index (PN_ACTUATOR (displacement),
|
|
161 PN_DISPLACEMENT_OPT_FRAME_SCRIPT);
|
|
162
|
|
163 pn_script_parse_string (displacement->init_script, displacement->symbol_table,
|
|
164 pn_string_option_get_value (init_script_opt));
|
|
165 pn_script_parse_string (displacement->frame_script, displacement->symbol_table,
|
|
166 pn_string_option_get_value (frame_script_opt));
|
|
167
|
|
168 /* Set up 0 displacement */
|
|
169 PN_VARIABLE_VALUE (displacement->x_var) = 0.0;
|
|
170 PN_VARIABLE_VALUE (displacement->y_var) = 0.0;
|
|
171
|
|
172 /* Run the init script */
|
|
173 pn_script_execute (displacement->init_script);
|
|
174
|
|
175 if (PN_ACTUATOR_CLASS (parent_class)->prepare)
|
|
176 PN_ACTUATOR_CLASS (parent_class)->prepare (PN_ACTUATOR (displacement), image);
|
|
177 }
|
|
178
|
|
179 static void
|
|
180 pn_displacement_exec (PnImage *image, gint offset, guint nw, guint ne, guint sw, guint se,
|
|
181 gint startx, gint starty, gint endx, gint endy)
|
|
182 {
|
|
183 gint i, j, width, height, stride;
|
|
184 PnColor *src, *dest, zero = {0,0,0,0};
|
|
185 guint r, g, b;
|
|
186
|
|
187 width = pn_image_get_width (image);
|
|
188 height = pn_image_get_height (image);
|
|
189 stride = pn_image_get_pitch (image) >> 2;
|
|
190
|
|
191 src = pn_image_get_image_buffer (image);
|
|
192 dest = pn_image_get_transform_buffer (image);
|
|
193
|
|
194 for (j=0; j<height; j++)
|
|
195 for (i=0; i<width; i++)
|
|
196 {
|
|
197 if (i < startx || i >= endx
|
|
198 || j < starty || j >= endy)
|
|
199 dest[j * stride + i] = zero;
|
|
200 else
|
|
201 {
|
|
202 r = src[(j * stride + i) + offset].red * nw;
|
|
203 g = src[(j * stride + i) + offset].green * nw;
|
|
204 b = src[(j * stride + i) + offset].blue * nw;
|
|
205
|
|
206 r += src[(j * stride + (i + 1)) + offset].red * ne;
|
|
207 g += src[(j * stride + (i + 1)) + offset].green * ne;
|
|
208 b += src[(j * stride + (i + 1)) + offset].blue * ne;
|
|
209
|
|
210 r += src[((j + 1) * stride + i) + offset].red * sw;
|
|
211 g += src[((j + 1) * stride + i) + offset].green * sw;
|
|
212 b += src[((j + 1) * stride + i) + offset].blue * sw;
|
|
213
|
|
214 r += src[((j + 1) * stride + (i + 1)) + offset].red * se;
|
|
215 g += src[((j + 1) * stride + (i + 1)) + offset].green * se;
|
|
216 b += src[((j + 1) * stride + (i + 1)) + offset].blue * se;
|
|
217
|
|
218 dest[j * stride + i].red = r >> 7;
|
|
219 dest[j * stride + i].green = g >> 7;
|
|
220 dest[j * stride + i].blue = b >> 7;
|
|
221 }
|
|
222 }
|
|
223 }
|
|
224
|
|
225 #ifdef PN_USE_MMX
|
|
226 static void
|
|
227 pn_displacement_exec_mmx (PnImage *image, gint offset, guint nw, guint ne, guint sw, guint se,
|
|
228 gint startx, gint starty, gint endx, gint endy)
|
|
229 {
|
|
230 PnColor *src, *dest, *endptr;
|
|
231 gint width, height, stride, i;
|
|
232 guint64 upnw; /* a place to store the unpacked se weight */
|
|
233
|
|
234 width = pn_image_get_width (image);
|
|
235 height = pn_image_get_height (image);
|
|
236 stride = pn_image_get_pitch (image) >> 2;
|
|
237
|
|
238 src = pn_image_get_image_buffer (image);
|
|
239 dest = pn_image_get_transform_buffer (image);
|
|
240
|
|
241 memset (dest, 0, pn_image_get_pitch (image) * starty);
|
|
242 for (i=MAX(starty, 0); i<endy; i++)
|
|
243 {
|
|
244 memset (dest + i * stride, 0, startx * sizeof (PnColor));
|
|
245 memset (dest + i * stride + endx, 0, (width - endx) * sizeof (PnColor));
|
|
246 }
|
|
247 memset (dest + endy * stride, 0, (height - endy) * pn_image_get_pitch (image));
|
|
248
|
|
249 src += (starty * stride) + offset;
|
|
250 endptr = src + endx;
|
|
251 src += startx;
|
|
252 dest += (starty * stride) + startx;
|
|
253
|
|
254 __asm__ __volatile__ (
|
|
255 /* Unpack the weights */
|
|
256 "pxor %%mm7,%%mm7\n\t" /* zero mm7 for unpacking */
|
|
257 "movd %8,%%mm4\n\t" /* load the weights */
|
|
258 "punpcklbw %%mm7,%%mm4\n\t" /* unpack the weights to words */
|
|
259 "movq %%mm4,%%mm6\n\t" /* copy the weights for separate expansion */
|
|
260 "punpcklwd %%mm4,%%mm4\n\t" /* expand the top weights */
|
|
261 "punpckhwd %%mm6,%%mm6\n\t" /* expand the bottom weights */
|
|
262 "movq %%mm4,%%mm5\n\t" /* copy the top pixels for separate expansion */
|
|
263 "movq %%mm6,%%mm7\n\t" /* copy the bottom pixels for separate expansion */
|
|
264 "punpckldq %%mm4,%%mm4\n\t" /* expand the NW weight */
|
|
265 "punpckhdq %%mm5,%%mm5\n\t" /* expand the NE weight */
|
|
266 "punpckldq %%mm6,%%mm6\n\t" /* expand the SW weight */
|
|
267 "punpckhdq %%mm7,%%mm7\n\t" /* expand the SE weight */
|
|
268 "movq %%mm7,%4\n\t" /* save the SE weight so we can use mm7 as 0 */
|
|
269 "pxor %%mm7,%%mm7\n\t" /* re-zero mm7 */
|
|
270
|
|
271 "10:\n\t"
|
|
272
|
|
273 /* zero mm7 for unpacking */
|
|
274 "pxor %%mm7,%%mm7\n\t"
|
|
275
|
|
276 /* load & unpack the pixels */
|
|
277 "movd (%0),%%mm0\n\t"
|
|
278 "punpcklbw %%mm7,%%mm0\n\t"
|
|
279 "movd 4(%0),%%mm1\n\t"
|
|
280 "punpcklbw %%mm7,%%mm1\n\t"
|
|
281 "movd (%0,%5),%%mm2\n\t"
|
|
282 "punpcklbw %%mm7,%%mm2\n\t"
|
|
283 "movd 4(%0,%5),%%mm3\n\t"
|
|
284 "punpcklbw %%mm7,%%mm3\n\t"
|
|
285
|
|
286 /* reload the unpacked se weight */
|
|
287 "movq %4,%%mm7\n\t"
|
|
288
|
|
289 /* weight each pixel */
|
|
290 "pmullw %%mm4,%%mm0\n\t"
|
|
291 "pmullw %%mm5,%%mm1\n\t"
|
|
292 "pmullw %%mm6,%%mm2\n\t"
|
|
293 "pmullw %%mm7,%%mm3\n\t"
|
|
294
|
|
295 /* add up the weighted pixels */
|
|
296 "paddw %%mm1,%%mm0\n\t"
|
|
297 "paddw %%mm2,%%mm0\n\t"
|
|
298 "paddw %%mm3,%%mm0\n\t"
|
|
299
|
|
300 /* normalize the resulting pixel, pack it, and write it */
|
|
301 "psrlw $7,%%mm0\n\t"
|
|
302 "packuswb %%mm0,%%mm0\n\t"
|
|
303 "movd %%mm0,(%1)\n\t"
|
|
304
|
|
305 /* advance the pointers */
|
|
306 "addl $4,%0\n\t"
|
|
307 "addl $4,%1\n\t"
|
|
308
|
|
309 /* see if we're done with this line */
|
|
310 "cmpl %3,%0\n\t"
|
|
311 "jl 10b\n\t"
|
|
312
|
|
313 /* advance the pointers */
|
|
314 "addl %7,%0\n\t"
|
|
315 "addl %7,%1\n\t"
|
|
316 "addl %5,%3\n\t"
|
|
317
|
|
318 /* see if we're done */
|
|
319 "incl %2\n\t"
|
|
320 "cmpl %6,%2\n\t"
|
|
321 "jl 10b\n\t"
|
|
322
|
|
323 "emms\n\t"
|
|
324
|
|
325 : /* no outputs */
|
|
326 : "r" (src), /* 0 */
|
|
327 "r" (dest), /* 1 */
|
|
328 "r" (starty), /* 2 */
|
|
329 "m" (endptr), /* 3 */
|
|
330 "m" (upnw), /* 4 */
|
|
331 "r" (pn_image_get_pitch (image)), /* 5 */
|
|
332 "r" (endy), /* 6 */
|
|
333 "rm" (((stride - endx) + startx) * sizeof (PnColor)), /* 7 */
|
|
334 "rm" ((guint32)((se << 24) | (sw << 16) | (ne << 8) | nw ))); /* 8 */
|
|
335 }
|
|
336 #endif /* PN_USE_MMX */
|
|
337
|
|
338 static void
|
|
339 pn_displacement_execute (PnDisplacement *displacement, PnImage *image, PnAudioData *audio_data)
|
|
340 {
|
|
341 g_return_if_fail (displacement != NULL);
|
|
342 g_return_if_fail (PN_IS_DISPLACEMENT (displacement));
|
|
343 g_return_if_fail (image != NULL);
|
|
344 g_return_if_fail (PN_IS_IMAGE (image));
|
|
345 g_return_if_fail (audio_data != NULL);
|
|
346 g_return_if_fail (PN_IS_AUDIO_DATA (audio_data));
|
|
347
|
|
348 /* set up the volume in-script variable */
|
|
349 PN_VARIABLE_VALUE (displacement->volume_var) = pn_audio_data_get_volume (audio_data);
|
|
350
|
|
351 /* run the frame scipt */
|
|
352 pn_script_execute (displacement->frame_script);
|
|
353
|
|
354 if (fabs (PN_VARIABLE_VALUE (displacement->x_var)) <= 2.0
|
|
355 && fabs (PN_VARIABLE_VALUE (displacement->y_var)) <= 2.0)
|
|
356 {
|
|
357 guint nw, ne, sw, se;
|
|
358 gint startx, starty, endx, endy;
|
|
359 gint offset;
|
|
360 gdouble x, y, xfrac, yfrac;
|
|
361
|
|
362 x = -PN_VARIABLE_VALUE (displacement->x_var) * (pn_image_get_width (image) >> 1);
|
|
363 y = PN_VARIABLE_VALUE (displacement->y_var) * (pn_image_get_height (image) >> 1);
|
|
364
|
|
365 xfrac = modf (x, &x);
|
|
366 yfrac = modf (y, &y);
|
|
367
|
|
368 if (x < 0.0 || xfrac < 0.0)
|
|
369 {
|
|
370 x--;
|
|
371 xfrac++;
|
|
372 }
|
|
373 if (y < 0.0 || yfrac < 0.0)
|
|
374 {
|
|
375 y--;
|
|
376 yfrac++;
|
|
377 }
|
|
378
|
|
379 se = xfrac * yfrac * 128.0;
|
|
380 sw = (1.0 - xfrac) * yfrac * 128.0;
|
|
381 ne = xfrac * (1.0 - yfrac) * 128.0;
|
|
382 nw = 128 - (se + sw + ne);
|
|
383
|
|
384 offset = (y * (pn_image_get_pitch (image) >> 2)) + x;
|
|
385
|
|
386 startx = -x;
|
|
387 starty = -y;
|
|
388 endx = pn_image_get_width (image) - (x+1);
|
|
389 endy = pn_image_get_height (image) - (y+1);
|
|
390
|
|
391 startx = MAX (startx, 0);
|
|
392 starty = MAX (starty, 0);
|
|
393 endx = MIN (endx, pn_image_get_width (image));
|
|
394 endy = MIN (endy, pn_image_get_height (image));
|
|
395
|
|
396 /* I'm too lazy to special case the rightmost & bottommost pixels */
|
|
397 if (endx == pn_image_get_width (image))
|
|
398 endx--;
|
|
399 if (endy == pn_image_get_height (image))
|
|
400 endy--;
|
|
401
|
|
402 #ifdef PN_USE_MMX
|
|
403 if (pn_cpu_get_caps () & PN_CPU_CAP_MMX)
|
|
404 pn_displacement_exec_mmx (image, offset, nw, ne, sw, se, startx, starty, endx, endy);
|
|
405 else
|
|
406 #endif /* PN_USE_MMX */
|
|
407 pn_displacement_exec (image, offset, nw, ne, sw, se, startx, starty, endx, endy);
|
|
408 }
|
|
409 else
|
|
410 memset (pn_image_get_transform_buffer (image), 0,
|
|
411 pn_image_get_pitch (image) * pn_image_get_height (image));
|
|
412
|
|
413 pn_image_apply_transform (image);
|
|
414
|
|
415 if (PN_ACTUATOR_CLASS (parent_class)->execute)
|
|
416 PN_ACTUATOR_CLASS (parent_class)->execute (PN_ACTUATOR (displacement), image, audio_data);
|
|
417 }
|
|
418
|
|
419 PnDisplacement*
|
|
420 pn_displacement_new (void)
|
|
421 {
|
|
422 return (PnDisplacement *) g_object_new (PN_TYPE_DISPLACEMENT, NULL);
|
|
423 }
|