Mercurial > audlegacy-plugins
comparison src/paranormal-ng/xform.c @ 2078:1fa3c8cd366a
paranormal-ng: a GL visualiser. lots to do, most stuff won't work, but hey, this will do cool stuff too soon
author | William Pitcock <nenolod@atheme.org> |
---|---|
date | Mon, 15 Oct 2007 06:20:13 -0500 |
parents | |
children | 9084e2e05f4a |
comparison
equal
deleted
inserted
replaced
2077:e5b639ab62b0 | 2078:1fa3c8cd366a |
---|---|
1 /* | |
2 * paranormal: iterated pipeline-driven visualization plugin | |
3 * Copyright (c) 2006, 2007 William Pitcock <nenolod@dereferenced.org> | |
4 * Portions copyright (c) 2001 Jamie Gennis <jgennis@mindspring.com> | |
5 * | |
6 * This program is free software; you can redistribute it and/or modify | |
7 * it under the terms of the GNU General Public License as published by | |
8 * the Free Software Foundation; under version 2 of the License. | |
9 * | |
10 * This program is distributed in the hope that it will be useful, | |
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
13 * GNU General Public License for more details. | |
14 * | |
15 * You should have received a copy of the GNU General Public License | |
16 * along with this program; if not, write to the Free Software | |
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
18 */ | |
19 | |
20 /* FIXME: allow for only using an xform on part of the img? */ | |
21 /* FIXME: perhaps combine these into a single vector field | |
22 so that only 1 apply_xform needs to be done for as many | |
23 of these as someone wants to use */ | |
24 | |
25 #include <config.h> | |
26 | |
27 #include <math.h> | |
28 | |
29 #include <glib.h> | |
30 | |
31 #include "paranormal.h" | |
32 #include "actuators.h" | |
33 #include "pn_utils.h" | |
34 | |
35 #include "libcalc/calc.h" | |
36 | |
37 struct xform_vector | |
38 { | |
39 gint32 offset; /* the offset of the top left pixel */ | |
40 guint16 w; /* 4:4:4:4 NE, NW, SE, SW pixel weights | |
41 The total should be 16 */ | |
42 | |
43 /* if offset < 0 then w is the color index to | |
44 which the pixel will be set */ | |
45 }; | |
46 | |
47 static void | |
48 xfvec (float x, float y, struct xform_vector *v) | |
49 { | |
50 float xd, yd; | |
51 int weight[4]; | |
52 | |
53 if (x >= pn_image_data->width-1 || y >= pn_image_data->height-1 | |
54 || x < 0 || y < 0) | |
55 { | |
56 v->offset = -1; | |
57 v->w = 0; | |
58 return; | |
59 } | |
60 | |
61 v->offset = PN_IMG_INDEX (floor(x), floor(y)); | |
62 | |
63 xd = x - floor (x); | |
64 yd = y - floor (y); | |
65 | |
66 weight[3] = xd * yd * 16; | |
67 weight[2] = (1-xd) * yd * 16; | |
68 weight[1] = xd * (1-yd) * 16; | |
69 weight[0] = 16 - weight[3] - weight[2] - weight[1]; /* just in case */ | |
70 | |
71 v->w = (weight[0]<<12) | (weight[1]<<8) | (weight[2]<<4) | weight[3]; | |
72 } | |
73 | |
74 static void | |
75 apply_xform (struct xform_vector *vfield) | |
76 { | |
77 int i; | |
78 struct xform_vector *v; | |
79 register guchar *srcptr; | |
80 register guchar *destptr; | |
81 register int color; | |
82 | |
83 if (vfield == NULL) | |
84 return; | |
85 | |
86 for (i=0, v=vfield, destptr=pn_image_data->surface[1]; | |
87 i<pn_image_data->width*pn_image_data->height; | |
88 i++, v++, destptr++) | |
89 { | |
90 /* off the screen */ | |
91 if (v->offset < 0) | |
92 { | |
93 *destptr = (guchar)v->w; | |
94 continue; | |
95 } | |
96 | |
97 srcptr = pn_image_data->surface[0] + v->offset; | |
98 | |
99 /* exactly on the pixel */ | |
100 if (v->w == 0) | |
101 *destptr = *srcptr; | |
102 | |
103 /* gotta blend the points */ | |
104 else | |
105 { | |
106 color = *srcptr * (v->w>>12); | |
107 color += *++srcptr * ((v->w>>8) & 0x0f); | |
108 color += *(srcptr+=pn_image_data->width) * (v->w & 0x0f); | |
109 color += *(--srcptr) * ((v->w>>4) & 0x0f); | |
110 color >>= 4; | |
111 *destptr = (guchar)color; | |
112 } | |
113 } | |
114 } | |
115 | |
116 /* **************** xform_spin **************** */ | |
117 /* FIXME: Describe these better, how they are backwards */ | |
118 /* FIXME: better name? */ | |
119 struct pn_actuator_option_desc xform_spin_opts[] = | |
120 { | |
121 { "angle", "The angle of rotation", OPT_TYPE_FLOAT, { fval: -8.0 } }, | |
122 { "r_add", "The number of pixels by which the r coordinate will be " | |
123 "increased (before scaling)", OPT_TYPE_FLOAT, { fval: 0.0 } }, | |
124 { "r_scale", "The amount by which the r coordinate of each pixel will " | |
125 "be scaled", OPT_TYPE_FLOAT, { fval: 1.0 } }, | |
126 { NULL } | |
127 }; | |
128 | |
129 struct xform_spin_data | |
130 { | |
131 int width, height; | |
132 struct xform_vector *vfield; | |
133 }; | |
134 | |
135 static void | |
136 xform_spin_init (gpointer *data) | |
137 { | |
138 *data = g_new0 (struct xform_spin_data, 1); | |
139 } | |
140 | |
141 static void | |
142 xform_spin_cleanup (gpointer data) | |
143 { | |
144 struct xform_spin_data *d = (struct xform_spin_data *) data; | |
145 | |
146 | |
147 if (d) | |
148 { | |
149 if (d->vfield) | |
150 g_free (d->vfield); | |
151 g_free (d); | |
152 } | |
153 } | |
154 | |
155 static void | |
156 xform_spin_exec (const struct pn_actuator_option *opts, | |
157 gpointer data) | |
158 { | |
159 struct xform_spin_data *d = (struct xform_spin_data*)data; | |
160 float i, j; | |
161 | |
162 if (d->width != pn_image_data->width | |
163 || d->height != pn_image_data->height) | |
164 { | |
165 d->width = pn_image_data->width; | |
166 d->height = pn_image_data->height; | |
167 | |
168 if (d->vfield) | |
169 g_free (d->vfield); | |
170 | |
171 d->vfield = g_malloc0 (sizeof(struct xform_vector) | |
172 * d->width * d->height); | |
173 | |
174 for (j=-(pn_image_data->height>>1)+1; j<=pn_image_data->height>>1; j++) | |
175 for (i=-(pn_image_data->width>>1); i<pn_image_data->width>>1; i++) | |
176 { | |
177 float r, t = 0; | |
178 float x, y; | |
179 | |
180 r = sqrt (i*i + j*j); | |
181 if (r) | |
182 t = asin (j/r); | |
183 if (i < 0) | |
184 t = M_PI - t; | |
185 | |
186 t += opts[0].val.fval * M_PI/180.0; | |
187 r += opts[1].val.fval; | |
188 r *= opts[2].val.fval; | |
189 | |
190 x = (r * cos (t)) + (pn_image_data->width>>1); | |
191 y = (pn_image_data->height>>1) - (r * sin (t)); | |
192 | |
193 xfvec (x, y, &d->vfield | |
194 [PN_IMG_INDEX ((pn_image_data->width>>1)+(int)rint(i), | |
195 ((pn_image_data->height>>1)-(int)rint(j)))]); | |
196 } | |
197 } | |
198 | |
199 apply_xform (d->vfield); | |
200 pn_swap_surfaces (); | |
201 } | |
202 | |
203 struct pn_actuator_desc builtin_xform_spin = | |
204 { | |
205 "xform_spin", "Spin Transform", | |
206 "Rotates and radially scales the image", | |
207 0, xform_spin_opts, | |
208 xform_spin_init, xform_spin_cleanup, xform_spin_exec | |
209 }; | |
210 | |
211 /* **************** xform_ripple **************** */ | |
212 struct pn_actuator_option_desc xform_ripple_opts[] = | |
213 { | |
214 { "angle", "The angle of rotation", OPT_TYPE_FLOAT, { fval: 0 } }, | |
215 { "ripples", "The number of ripples that fit on the screen " | |
216 "(horizontally)", OPT_TYPE_FLOAT, { fval: 8 } }, | |
217 { "base_speed", "The minimum number of pixels to move each pixel", | |
218 OPT_TYPE_FLOAT, { fval: 1 } }, | |
219 { "mod_speed", "The maximum number of pixels by which base_speed" | |
220 " will be modified", OPT_TYPE_FLOAT, { fval: 1 } }, | |
221 { NULL } | |
222 }; | |
223 | |
224 struct xform_ripple_data | |
225 { | |
226 int width, height; | |
227 struct xform_vector *vfield; | |
228 }; | |
229 | |
230 static void | |
231 xform_ripple_init (gpointer *data) | |
232 { | |
233 *data = g_new0 (struct xform_ripple_data, 1); | |
234 } | |
235 | |
236 static void | |
237 xform_ripple_cleanup (gpointer data) | |
238 { | |
239 struct xform_ripple_data *d = (struct xform_ripple_data*) data; | |
240 | |
241 if (d) | |
242 { | |
243 if (d->vfield) | |
244 g_free (d->vfield); | |
245 g_free (d); | |
246 } | |
247 } | |
248 | |
249 static void | |
250 xform_ripple_exec (const struct pn_actuator_option *opts, | |
251 gpointer data) | |
252 { | |
253 struct xform_ripple_data *d = (struct xform_ripple_data*)data; | |
254 float i, j; | |
255 | |
256 if (d->width != pn_image_data->width | |
257 || d->height != pn_image_data->height) | |
258 { | |
259 d->width = pn_image_data->width; | |
260 d->height = pn_image_data->height; | |
261 | |
262 if (d->vfield) | |
263 g_free (d->vfield); | |
264 | |
265 d->vfield = g_malloc (sizeof(struct xform_vector) | |
266 * d->width * d->height); | |
267 | |
268 for (j=-(pn_image_data->height>>1)+1; j<=pn_image_data->height>>1; j++) | |
269 for (i=-(pn_image_data->width>>1); i<pn_image_data->width>>1; i++) | |
270 { | |
271 float r, t = 0; | |
272 float x, y; | |
273 | |
274 r = sqrt (i*i + j*j); | |
275 if (r) | |
276 t = asin (j/r); | |
277 if (i < 0) | |
278 t = M_PI - t; | |
279 | |
280 t += opts[0].val.fval * M_PI/180.0; | |
281 | |
282 if (r > 4)//(pn_image_data->width/(2*opts[1].val.fval))) | |
283 r -= opts[2].val.fval + (opts[3].val.fval/2) * | |
284 (1 + sin ((r/(pn_image_data->width/(2*opts[1].val.fval)))*M_PI)); | |
285 /* else if (r > 4) */ | |
286 /* r *= r/(pn_image_data->width/opts[1].val.fval); */ | |
287 else /* don't let it explode */ | |
288 r = 1000000; | |
289 | |
290 | |
291 x = (r * cos (t)) + (pn_image_data->width>>1); | |
292 y = (pn_image_data->height>>1) - (r * sin (t)); | |
293 | |
294 xfvec (x, y, &d->vfield | |
295 [PN_IMG_INDEX ((pn_image_data->width>>1)+(int)rint(i), | |
296 ((pn_image_data->height>>1)-(int)rint(j)))]); | |
297 } | |
298 } | |
299 | |
300 apply_xform (d->vfield); | |
301 pn_swap_surfaces (); | |
302 } | |
303 | |
304 struct pn_actuator_desc builtin_xform_ripple = | |
305 { | |
306 "xform_ripple", "Ripple Transform", "Creates an ripple effect", | |
307 0, xform_ripple_opts, | |
308 xform_ripple_init, xform_ripple_cleanup, xform_ripple_exec | |
309 }; | |
310 | |
311 /* **************** xform_bump_spin **************** */ | |
312 struct pn_actuator_option_desc xform_bump_spin_opts[] = | |
313 { | |
314 { "angle", "The angle of rotation", OPT_TYPE_FLOAT, { fval: 0 } }, | |
315 { "bumps", "The number of bumps that on the image", | |
316 OPT_TYPE_FLOAT, { fval: 8 } }, | |
317 { "base_scale", "The base radial scale", | |
318 OPT_TYPE_FLOAT, { fval: 0.95 } }, | |
319 { "mod_scale", "The maximum amount that should be " | |
320 "added to the base_scale to create the 'bump' effect", | |
321 OPT_TYPE_FLOAT, { fval: .1 } }, | |
322 { NULL } | |
323 }; | |
324 | |
325 struct xform_bump_spin_data | |
326 { | |
327 int width, height; | |
328 struct xform_vector *vfield; | |
329 }; | |
330 | |
331 static void | |
332 xform_bump_spin_init (gpointer *data) | |
333 { | |
334 *data = g_new0 (struct xform_bump_spin_data, 1); | |
335 } | |
336 | |
337 static void | |
338 xform_bump_spin_cleanup (gpointer data) | |
339 { | |
340 struct xform_bump_spin_data *d = (struct xform_bump_spin_data*) data; | |
341 | |
342 if (d) | |
343 { | |
344 if (d->vfield) | |
345 g_free (d->vfield); | |
346 g_free (d); | |
347 } | |
348 } | |
349 | |
350 static void | |
351 xform_bump_spin_exec (const struct pn_actuator_option *opts, | |
352 gpointer data) | |
353 { | |
354 struct xform_bump_spin_data *d = (struct xform_bump_spin_data*)data; | |
355 float i, j; | |
356 | |
357 if (d->width != pn_image_data->width | |
358 || d->height != pn_image_data->height) | |
359 { | |
360 d->width = pn_image_data->width; | |
361 d->height = pn_image_data->height; | |
362 | |
363 if (d->vfield) | |
364 g_free (d->vfield); | |
365 | |
366 d->vfield = g_malloc (sizeof(struct xform_vector) | |
367 * d->width * d->height); | |
368 | |
369 for (j=-(pn_image_data->height>>1)+1; j<=pn_image_data->height>>1; j++) | |
370 for (i=-(pn_image_data->width>>1); i<pn_image_data->width>>1; i++) | |
371 { | |
372 float r, t = 0; | |
373 float x, y; | |
374 | |
375 r = sqrt (i*i + j*j); | |
376 if (r) | |
377 t = asin (j/r); | |
378 if (i < 0) | |
379 t = M_PI - t; | |
380 | |
381 t += opts[0].val.fval * M_PI/180.0; | |
382 | |
383 r *= opts[2].val.fval + opts[3].val.fval | |
384 * (1 + sin (t*opts[1].val.fval)); | |
385 | |
386 x = (r * cos (t)) + (pn_image_data->width>>1); | |
387 y = (pn_image_data->height>>1) - (r * sin (t)); | |
388 | |
389 xfvec (x, y, &d->vfield | |
390 [PN_IMG_INDEX ((pn_image_data->width>>1)+(int)rint(i), | |
391 ((pn_image_data->height>>1)-(int)rint(j)))]); | |
392 } | |
393 } | |
394 | |
395 apply_xform (d->vfield); | |
396 pn_swap_surfaces (); | |
397 } | |
398 | |
399 struct pn_actuator_desc builtin_xform_bump_spin = | |
400 { | |
401 "xform_bump_spin", "Bump Transform", | |
402 "Rotate the image at a varying speed to create " | |
403 "the illusion of bumps", | |
404 0, xform_bump_spin_opts, | |
405 xform_bump_spin_init, xform_bump_spin_cleanup, xform_bump_spin_exec | |
406 }; | |
407 | |
408 /* **************** xform_halfrender **************** */ | |
409 struct pn_actuator_option_desc xform_halfrender_opts[] = | |
410 { | |
411 { "direction", "Negative is horizontal, positive is vertical.", | |
412 OPT_TYPE_INT, { ival: 1 } }, | |
413 { "render_twice", "Render the second image.", | |
414 OPT_TYPE_BOOLEAN, { bval: TRUE } }, | |
415 { NULL } | |
416 }; | |
417 | |
418 static void | |
419 xform_halfrender_exec (const struct pn_actuator_option *opts, | |
420 gpointer data) | |
421 { | |
422 gint x, y; | |
423 | |
424 if (opts[0].val.ival < 0) | |
425 { | |
426 for (y = 0; y < pn_image_data->height; y += 2) | |
427 { | |
428 for (x = 0; x < pn_image_data->width; x++) | |
429 { | |
430 pn_image_data->surface[1][PN_IMG_INDEX(x, y / 2)] = | |
431 pn_image_data->surface[0][PN_IMG_INDEX(x, y)]; | |
432 if (opts[1].val.bval) | |
433 { | |
434 pn_image_data->surface[1][PN_IMG_INDEX(x, (y / 2) + (pn_image_data->height / 2))] = | |
435 pn_image_data->surface[0][PN_IMG_INDEX(x, y)]; | |
436 } | |
437 } | |
438 } | |
439 } | |
440 else | |
441 { | |
442 for (y = 0; y < pn_image_data->height; y++) | |
443 { | |
444 for (x = 0; x < pn_image_data->width; x += 2) | |
445 { | |
446 pn_image_data->surface[1][PN_IMG_INDEX(x / 2, y)] = | |
447 pn_image_data->surface[0][PN_IMG_INDEX(x, y)]; | |
448 if (opts[1].val.bval) | |
449 { | |
450 pn_image_data->surface[1][PN_IMG_INDEX((x / 2) + (pn_image_data->width / 2), y)] = | |
451 pn_image_data->surface[0][PN_IMG_INDEX(x, y)]; | |
452 } | |
453 } | |
454 } | |
455 } | |
456 | |
457 pn_swap_surfaces (); | |
458 } | |
459 | |
460 struct pn_actuator_desc builtin_xform_halfrender = | |
461 { | |
462 "xform_halfrender", "Halfrender Transform", | |
463 "Divides the surface in half and renders it twice.", | |
464 0, xform_halfrender_opts, | |
465 NULL, NULL, xform_halfrender_exec | |
466 }; | |
467 | |
468 /* **************** xform_movement **************** */ | |
469 struct pn_actuator_option_desc xform_movement_opts[] = | |
470 { | |
471 { "formula", "The formula to evaluate.", | |
472 OPT_TYPE_STRING, { sval: "r = r * cos(r); d = sin(d);" } }, | |
473 { "polar", "Whether the coordinates are polar or not.", | |
474 OPT_TYPE_BOOLEAN, { bval: TRUE } }, | |
475 { NULL } | |
476 }; | |
477 | |
478 typedef struct { | |
479 int width, height; /* Previous width and height. */ | |
480 struct xform_vector *vfield; | |
481 } PnMovementData; | |
482 | |
483 static void | |
484 xform_movement_init (gpointer *data) | |
485 { | |
486 *data = g_new0(PnMovementData, 1); | |
487 } | |
488 | |
489 static void | |
490 xform_movement_cleanup (gpointer data) | |
491 { | |
492 PnMovementData *d = (PnMovementData *) data; | |
493 | |
494 if (d) | |
495 { | |
496 if (d->vfield) | |
497 g_free (d->vfield); | |
498 g_free (d); | |
499 } | |
500 } | |
501 | |
502 inline void | |
503 xform_trans_polar (struct xform_vector *vfield, gint x, gint y, | |
504 expression_t *expr, symbol_dict_t *dict) | |
505 { | |
506 gdouble *rf, *df; | |
507 gdouble xf, yf; | |
508 gint xn, yn; | |
509 | |
510 rf = dict_variable(dict, "r"); | |
511 df = dict_variable(dict, "d"); | |
512 | |
513 /* Points (xf, yf) must be in a (-1..1) square. */ | |
514 xf = 2.0 * x / (pn_image_data->width - 1) - 1.0; | |
515 yf = 2.0 * y / (pn_image_data->height - 1) - 1.0; | |
516 | |
517 /* Now, convert to polar coordinates r and d. */ | |
518 *rf = hypot(xf, yf); | |
519 *df = atan2(yf, xf); | |
520 | |
521 /* Run the script. */ | |
522 expr_execute(expr, dict); | |
523 | |
524 /* Back to (-1..1) square. */ | |
525 xf = (*rf) * cos ((*df)); | |
526 yf = (*rf) * sin ((*df)); | |
527 | |
528 /* Convert back to physical coordinates. */ | |
529 xn = (int)(((xf + 1.0) * (pn_image_data->width - 1) / 2) + 0.5); | |
530 yn = (int)(((yf + 1.0) * (pn_image_data->height - 1) / 2) + 0.5); | |
531 | |
532 if (xn < 0 || xn >= pn_image_data->width || yn < 0 || yn >= pn_image_data->height) | |
533 { | |
534 xn = x; yn = y; | |
535 } | |
536 | |
537 xfvec (xn, yn, &vfield[PN_IMG_INDEX (x, y)]); | |
538 } | |
539 | |
540 inline void | |
541 xform_trans_literal (struct xform_vector *vfield, gint x, gint y, | |
542 expression_t *expr, symbol_dict_t *dict) | |
543 { | |
544 gdouble rf, df; | |
545 gdouble *xf, *yf; | |
546 gint xn, yn; | |
547 | |
548 xf = dict_variable(dict, "x"); | |
549 yf = dict_variable(dict, "y"); | |
550 | |
551 /* Points (xf, yf) must be in a (-1..1) square. */ | |
552 *xf = 2.0 * x / (pn_image_data->width - 1) - 1.0; | |
553 *yf = 2.0 * y / (pn_image_data->height - 1) - 1.0; | |
554 | |
555 /* Run the script. */ | |
556 expr_execute(expr, dict); | |
557 | |
558 /* Convert back to physical coordinates. */ | |
559 xn = (int)(((*xf + 1.0) * (pn_image_data->width - 1) / 2) + 0.5); | |
560 yn = (int)(((*yf + 1.0) * (pn_image_data->height - 1) / 2) + 0.5); | |
561 | |
562 if (xn < 0 || xn >= pn_image_data->width || yn < 0 || yn >= pn_image_data->height) | |
563 { | |
564 xn = x; yn = y; | |
565 } | |
566 | |
567 xfvec (xn, yn, &vfield[PN_IMG_INDEX (x, y)]); | |
568 } | |
569 | |
570 static void | |
571 xform_movement_exec (const struct pn_actuator_option *opts, | |
572 gpointer odata) | |
573 { | |
574 PnMovementData *d = (PnMovementData *) odata; | |
575 void (*transform_func)(struct xform_vector *, gint, gint, expression_t *, symbol_dict_t *) = | |
576 opts[1].val.bval == TRUE ? xform_trans_polar : xform_trans_literal; | |
577 | |
578 if (d->width != pn_image_data->width | |
579 || d->height != pn_image_data->height) | |
580 { | |
581 gint i, j; | |
582 gdouble *rf, *df; | |
583 gdouble xf, yf; | |
584 gint xn, yn; | |
585 expression_t *expr; | |
586 symbol_dict_t *dict; | |
587 | |
588 d->width = pn_image_data->width; | |
589 d->height = pn_image_data->height; | |
590 | |
591 if (d->vfield) | |
592 { | |
593 g_free (d->vfield); | |
594 d->vfield = NULL; | |
595 } | |
596 | |
597 if (opts[0].val.sval == NULL) | |
598 return; | |
599 | |
600 dict = dict_new(); | |
601 expr = expr_compile_string(opts[0].val.sval, dict); | |
602 if (!expr) | |
603 { | |
604 dict_free(dict); | |
605 return; | |
606 } | |
607 | |
608 rf = dict_variable(dict, "r"); | |
609 df = dict_variable(dict, "d"); | |
610 | |
611 d->vfield = g_malloc (sizeof(struct xform_vector) | |
612 * d->width * d->height); | |
613 | |
614 for (j = 0; j < pn_image_data->height; j++) | |
615 for (i = 0; i < pn_image_data->width; i++) | |
616 { | |
617 transform_func(d->vfield, i, j, expr, dict); | |
618 } | |
619 } | |
620 | |
621 apply_xform (d->vfield); | |
622 pn_swap_surfaces (); | |
623 } | |
624 | |
625 struct pn_actuator_desc builtin_xform_movement = | |
626 { | |
627 "xform_movement", "Movement Transform", | |
628 "A customizable blitter.", | |
629 0, xform_movement_opts, | |
630 xform_movement_init, xform_movement_cleanup, xform_movement_exec | |
631 }; | |
632 | |
633 /* **************** xform_dynmovement **************** */ | |
634 /* FIXME: really slow */ | |
635 struct pn_actuator_option_desc xform_dynmovement_opts[] = | |
636 { | |
637 { "init_script", "The formula to evaluate on init.", | |
638 OPT_TYPE_STRING, { sval: "" } }, | |
639 { "beat_script", "The formula to evaluate on each beat.", | |
640 OPT_TYPE_STRING, { sval: "" } }, | |
641 { "frame_script", "The formula to evaluate on each frame.", | |
642 OPT_TYPE_STRING, { sval: "" } }, | |
643 { "point_script", "The formula to evaluate.", | |
644 OPT_TYPE_STRING, { sval: "d = 0.15;" } }, | |
645 { "polar", "Whether or not the coordinates to use are polar.", | |
646 OPT_TYPE_BOOLEAN, { bval: TRUE } }, | |
647 { NULL } | |
648 }; | |
649 | |
650 typedef struct { | |
651 int width, height; /* Previous width and height. */ | |
652 expression_t *expr_init; | |
653 expression_t *expr_frame; | |
654 expression_t *expr_beat; | |
655 expression_t *expr_point; | |
656 symbol_dict_t *dict; | |
657 struct xform_vector *vfield; | |
658 } PnDynMovementData; | |
659 | |
660 static void | |
661 xform_dynmovement_init (gpointer *data) | |
662 { | |
663 *data = g_new0(PnDynMovementData, 1); | |
664 } | |
665 | |
666 static void | |
667 xform_dynmovement_cleanup (gpointer data) | |
668 { | |
669 PnDynMovementData *d = (PnDynMovementData *) data; | |
670 | |
671 if (d) | |
672 { | |
673 if (d->expr_init) | |
674 expr_free (d->expr_init); | |
675 if (d->expr_beat) | |
676 expr_free (d->expr_beat); | |
677 if (d->expr_frame) | |
678 expr_free (d->expr_frame); | |
679 if (d->expr_point) | |
680 expr_free (d->expr_point); | |
681 if (d->dict) | |
682 dict_free (d->dict); | |
683 if (d->vfield) | |
684 g_free (d->vfield); | |
685 g_free (d); | |
686 } | |
687 } | |
688 | |
689 static void | |
690 xform_dynmovement_exec (const struct pn_actuator_option *opts, | |
691 gpointer odata) | |
692 { | |
693 PnDynMovementData *d = (PnDynMovementData *) odata; | |
694 gint i, j; | |
695 gdouble *rf, *df; | |
696 gdouble xf, yf; | |
697 gint xn, yn; | |
698 void (*transform_func)(struct xform_vector *, gint, gint, expression_t *, symbol_dict_t *) = | |
699 opts[4].val.bval == TRUE ? xform_trans_polar : xform_trans_literal; | |
700 gboolean make_table = FALSE; | |
701 | |
702 if (d->width != pn_image_data->width | |
703 || d->height != pn_image_data->height) | |
704 { | |
705 d->width = pn_image_data->width; | |
706 d->height = pn_image_data->height; | |
707 | |
708 if (d->vfield) | |
709 { | |
710 g_free (d->vfield); | |
711 d->vfield = NULL; | |
712 } | |
713 | |
714 if (opts[3].val.sval == NULL) | |
715 return; | |
716 | |
717 if (!d->dict) | |
718 d->dict = dict_new(); | |
719 else | |
720 { | |
721 dict_free(d->dict); | |
722 d->dict = dict_new(); | |
723 } | |
724 | |
725 if (d->expr_init) | |
726 { | |
727 expr_free(d->expr_init); | |
728 d->expr_init = NULL; | |
729 } | |
730 | |
731 /* initialize */ | |
732 d->expr_init = expr_compile_string(opts[0].val.sval, d->dict); | |
733 | |
734 if (d->expr_init != NULL) | |
735 { | |
736 expr_execute(d->expr_init, d->dict); | |
737 } | |
738 | |
739 d->expr_beat = expr_compile_string(opts[1].val.sval, d->dict); | |
740 d->expr_frame = expr_compile_string(opts[2].val.sval, d->dict); | |
741 d->expr_point = expr_compile_string(opts[3].val.sval, d->dict); | |
742 | |
743 d->vfield = g_malloc (sizeof(struct xform_vector) | |
744 * d->width * d->height); | |
745 | |
746 make_table = TRUE; | |
747 } | |
748 | |
749 rf = dict_variable(d->dict, "r"); | |
750 df = dict_variable(d->dict, "d"); | |
751 | |
752 if (*opts[2].val.sval != '\0' || pn_new_beat) | |
753 make_table = TRUE; | |
754 | |
755 /* run the on-frame script. */ | |
756 if (make_table == TRUE) | |
757 { | |
758 if (d->expr_beat != NULL) | |
759 expr_execute(d->expr_beat, d->dict); | |
760 | |
761 if (d->expr_frame != NULL) | |
762 expr_execute(d->expr_frame, d->dict); | |
763 | |
764 for (j = 0; j < pn_image_data->height; j++) | |
765 for (i = 0; i < pn_image_data->width; i++) | |
766 { | |
767 transform_func(d->vfield, i, j, d->expr_point, d->dict); | |
768 } | |
769 } | |
770 | |
771 apply_xform (d->vfield); | |
772 pn_swap_surfaces (); | |
773 } | |
774 | |
775 struct pn_actuator_desc builtin_xform_dynmovement = | |
776 { | |
777 "xform_dynmovement", "Dynamic Movement Transform", | |
778 "A customizable blitter.", | |
779 0, xform_dynmovement_opts, | |
780 xform_dynmovement_init, xform_dynmovement_cleanup, xform_dynmovement_exec | |
781 }; |