1 /* GIMP - The GNU Image Manipulation Program
2 * Copyright (C) 1995 Spencer Kimball and Peter Mattis
3 *
4 * IfsCompose is a interface for creating IFS fractals by
5 * direct manipulation.
6 * Copyright (C) 1997 Owen Taylor
7 *
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <https://www.gnu.org/licenses/>.
19 */
20
21 /* TODO
22 * ----
23 *
24 * 1. Run in non-interactive mode (need to figure out useful way for a
25 * script to give the 19N parameters for an image). Perhaps just
26 * support saving parameters to a file, script passes file name.
27 * 2. Figure out if we need multiple phases for supersampled brushes.
28 */
29
30 #include "config.h"
31
32 #include <string.h>
33 #include <errno.h>
34
35 #include <glib/gstdio.h>
36
37 #include <libgimp/gimp.h>
38 #include <libgimp/gimpui.h>
39
40 #include "ifs-compose.h"
41
42 #include "libgimp/stdplugins-intl.h"
43
44
45 #define RESPONSE_RESET 1
46 #define RESPONSE_OPEN 2
47 #define RESPONSE_SAVE 3
48
49 #define DESIGN_AREA_MAX_SIZE 300
50
51 #define PREVIEW_RENDER_CHUNK 10000
52
53 #define UNDO_LEVELS 24
54
55 #define PLUG_IN_PARASITE "ifscompose-parasite"
56 #define PLUG_IN_PROC "plug-in-ifscompose"
57 #define PLUG_IN_BINARY "ifs-compose"
58 #define PLUG_IN_ROLE "gimp-ifs-compose"
59
60 typedef enum
61 {
62 OP_TRANSLATE,
63 OP_ROTATE, /* or scale */
64 OP_STRETCH
65 } DesignOp;
66
67 typedef enum
68 {
69 VALUE_PAIR_INT,
70 VALUE_PAIR_DOUBLE
71 } ValuePairType;
72
73 typedef struct
74 {
75 GtkAdjustment *adjustment;
76 GtkWidget *scale;
77 GtkWidget *spin;
78
79 ValuePairType type;
80 guint timeout_id;
81
82 union
83 {
84 gdouble *d;
85 gint *i;
86 } data;
87 } ValuePair;
88
89 typedef struct
90 {
91 IfsComposeVals ifsvals;
92 AffElement **elements;
93 gboolean *element_selected;
94 gint current_element;
95 } UndoItem;
96
97 typedef struct
98 {
99 GimpRGB *color;
100 GtkWidget *hbox;
101 GtkWidget *orig_preview;
102 GtkWidget *button;
103 gboolean fixed_point;
104 } ColorMap;
105
106 typedef struct
107 {
108 GtkWidget *dialog;
109
110 ValuePair *iterations_pair;
111 ValuePair *subdivide_pair;
112 ValuePair *radius_pair;
113 ValuePair *memory_pair;
114 } IfsOptionsDialog;
115
116 typedef struct
117 {
118 GtkWidget *area;
119 GtkUIManager *ui_manager;
120 GdkPixmap *pixmap;
121
122 DesignOp op;
123 gdouble op_x;
124 gdouble op_y;
125 gdouble op_xcenter;
126 gdouble op_ycenter;
127 gdouble op_center_x;
128 gdouble op_center_y;
129 guint button_state;
130 gint num_selected;
131 } IfsDesignArea;
132
133 typedef struct
134 {
135 ValuePair *prob_pair;
136 ValuePair *x_pair;
137 ValuePair *y_pair;
138 ValuePair *scale_pair;
139 ValuePair *angle_pair;
140 ValuePair *asym_pair;
141 ValuePair *shear_pair;
142 GtkWidget *flip_check_button;
143
144 ColorMap *red_cmap;
145 ColorMap *green_cmap;
146 ColorMap *blue_cmap;
147 ColorMap *black_cmap;
148 ColorMap *target_cmap;
149 ValuePair *hue_scale_pair;
150 ValuePair *value_scale_pair;
151 GtkWidget *simple_button;
152 GtkWidget *full_button;
153 GtkWidget *current_frame;
154
155 GtkWidget *preview;
156 guchar *preview_data;
157 gint preview_iterations;
158
159 gint drawable_width;
160 gint drawable_height;
161 gint preview_width;
162 gint preview_height;
163
164 AffElement *selected_orig;
165 gint current_element;
166 AffElementVals current_vals;
167
168 gboolean in_update; /* true if we're currently in
169 update_values() - don't do anything
170 on updates */
171 } IfsDialog;
172
173 typedef struct
174 {
175 gboolean run;
176 } IfsComposeInterface;
177
178 /* Declare local functions.
179 */
180 static void query (void);
181 static void run (const gchar *name,
182 gint nparams,
183 const GimpParam *param,
184 gint *nreturn_vals,
185 GimpParam **return_vals);
186
187 /* user interface functions */
188 static gint ifs_compose_dialog (gint32 drawable_id);
189 static void ifs_options_dialog (GtkWidget *parent);
190 static GtkWidget * ifs_compose_trans_page (void);
191 static GtkWidget * ifs_compose_color_page (void);
192 static GtkUIManager * design_op_menu_create (GtkWidget *window);
193 static void design_op_actions_update (void);
194 static void design_area_create (GtkWidget *window,
195 gint design_width,
196 gint design_height);
197
198 /* functions for drawing design window */
199 static void update_values (void);
200 static void set_current_element (gint index);
201 static void design_area_realize (GtkWidget *widget);
202 static gint design_area_expose (GtkWidget *widget,
203 GdkEventExpose *event);
204 static gint design_area_button_press (GtkWidget *widget,
205 GdkEventButton *event);
206 static gint design_area_button_release (GtkWidget *widget,
207 GdkEventButton *event);
208 static void design_area_select_all_callback (GtkWidget *widget,
209 gpointer data);
210 static gint design_area_configure (GtkWidget *widget,
211 GdkEventConfigure *event);
212 static gint design_area_motion (GtkWidget *widget,
213 GdkEventMotion *event);
214 static void design_area_redraw (void);
215
216 /* Undo ring functions */
217 static void undo_begin (void);
218 static void undo_update (gint element);
219 static void undo_exchange (gint el);
220 static void undo (void);
221 static void redo (void);
222
223 static void recompute_center (gboolean save_undo);
224 static void recompute_center_cb (GtkWidget *widget,
225 gpointer data);
226
227 static void ifs_compose (gint32 drawable_id);
228
229 static ColorMap *color_map_create (const gchar *name,
230 GimpRGB *orig_color,
231 GimpRGB *data,
232 gboolean fixed_point);
233 static void color_map_color_changed_cb (GtkWidget *widget,
234 ColorMap *color_map);
235 static void color_map_update (ColorMap *color_map);
236
237 /* interface functions */
238 static void simple_color_toggled (GtkWidget *widget, gpointer data);
239 static void simple_color_set_sensitive (void);
240 static void val_changed_update (void);
241 static ValuePair *value_pair_create (gpointer data,
242 gdouble lower,
243 gdouble upper,
244 gboolean create_scale,
245 ValuePairType type);
246 static void value_pair_update (ValuePair *value_pair);
247 static void value_pair_scale_callback (GtkAdjustment *adjustment,
248 ValuePair *value_pair);
249
250 static void design_op_update_callback (GtkRadioAction *action,
251 GtkRadioAction *current,
252 gpointer data);
253 static void flip_check_button_callback (GtkWidget *widget, gpointer data);
254 static gint preview_idle_render (gpointer data);
255
256 static void ifs_compose_preview (void);
257 static void ifs_compose_set_defaults (void);
258 static void ifs_compose_new_callback (GtkAction *action,
259 gpointer data);
260 static void ifs_compose_delete_callback (GtkAction *action,
261 gpointer data);
262 static void ifs_compose_options_callback (GtkAction *action,
263 gpointer data);
264 static void ifs_compose_load (GtkWidget *parent);
265 static void ifs_compose_save (GtkWidget *parent);
266 static void ifs_compose_response (GtkWidget *widget,
267 gint response_id,
268 gpointer data);
269
270 /*
271 * Some static variables
272 */
273
274 static IfsDialog *ifsD = NULL;
275 static IfsOptionsDialog *ifsOptD = NULL;
276 static IfsDesignArea *ifsDesign = NULL;
277
278
279 static AffElement **elements = NULL;
280 static gint *element_selected = NULL;
281 /* labels are generated by printing this int */
282 static gint count_for_naming = 0;
283
284 static UndoItem undo_ring[UNDO_LEVELS];
285 static gint undo_cur = -1;
286 static gint undo_num = 0;
287 static gint undo_start = 0;
288
289
290 /* num_elements = 0, signals not inited */
291 static IfsComposeVals ifsvals =
292 {
293 0, /* num_elements */
294 50000, /* iterations */
295 4096, /* max_memory */
296 4, /* subdivide */
297 0.75, /* radius */
298 1.0, /* aspect ratio */
299 0.5, /* center_x */
300 0.5, /* center_y */
301 };
302
303 static IfsComposeInterface ifscint =
304 {
305 FALSE, /* run */
306 };
307
308 const GimpPlugInInfo PLUG_IN_INFO =
309 {
310 NULL, /* init_proc */
311 NULL, /* quit_proc */
312 query, /* query_proc */
313 run, /* run_proc */
314 };
315
316
MAIN()317 MAIN ()
318
319 static void
320 query (void)
321 {
322 static const GimpParamDef args[] =
323 {
324 { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
325 { GIMP_PDB_IMAGE, "image", "Input image" },
326 { GIMP_PDB_DRAWABLE, "drawable", "Input drawable" },
327 };
328
329 static const GimpParamDef *return_vals = NULL;
330 static int nreturn_vals = 0;
331
332 gimp_install_procedure (PLUG_IN_PROC,
333 N_("Create an Iterated Function System (IFS) fractal"),
334 "Interactively create an Iterated Function System "
335 "fractal. Use the window on the upper left to adjust "
336 "the component transformations of the fractal. The "
337 "operation that is performed is selected by the "
338 "buttons underneath the window, or from a menu "
339 "popped up by the right mouse button. The fractal "
340 "will be rendered with a transparent background if "
341 "the current image has an alpha channel.",
342 "Owen Taylor",
343 "Owen Taylor",
344 "1997",
345 N_("_IFS Fractal..."),
346 "*",
347 GIMP_PLUGIN,
348 G_N_ELEMENTS (args), nreturn_vals,
349 args, return_vals);
350
351 gimp_plugin_menu_register (PLUG_IN_PROC,
352 "<Image>/Filters/Render/Fractals");
353 }
354
355 static void
run(const gchar * name,gint nparams,const GimpParam * param,gint * nreturn_vals,GimpParam ** return_vals)356 run (const gchar *name,
357 gint nparams,
358 const GimpParam *param,
359 gint *nreturn_vals,
360 GimpParam **return_vals)
361 {
362 static GimpParam values[1];
363 GimpRunMode run_mode;
364 GimpPDBStatusType status = GIMP_PDB_SUCCESS;
365 GimpParasite *parasite = NULL;
366 gint32 image_id;
367 gint32 drawable_id;
368 gboolean found_parasite = FALSE;
369
370 INIT_I18N ();
371 gegl_init (NULL, NULL);
372
373 run_mode = param[0].data.d_int32;
374
375 *nreturn_vals = 1;
376 *return_vals = values;
377
378 values[0].type = GIMP_PDB_STATUS;
379 values[0].data.d_status = status;
380
381 image_id = param[1].data.d_image;
382 drawable_id = param[2].data.d_drawable;
383
384 switch (run_mode)
385 {
386 case GIMP_RUN_INTERACTIVE:
387 /* Possibly retrieve data; first look for a parasite -
388 * if not found, fall back to global values
389 */
390 parasite = gimp_item_get_parasite (drawable_id,
391 PLUG_IN_PARASITE);
392 if (parasite)
393 {
394 found_parasite = ifsvals_parse_string (gimp_parasite_data (parasite),
395 &ifsvals, &elements);
396 gimp_parasite_free (parasite);
397 }
398
399 if (!found_parasite)
400 {
401 gint length = gimp_get_data_size (PLUG_IN_PROC);
402
403 if (length > 0)
404 {
405 gchar *data = g_new (gchar, length);
406
407 gimp_get_data (PLUG_IN_PROC, data);
408 ifsvals_parse_string (data, &ifsvals, &elements);
409 g_free (data);
410 }
411 }
412
413 /* after ifsvals_parse_string, need to set up naming */
414 count_for_naming = ifsvals.num_elements;
415
416 /* First acquire information with a dialog */
417 if (! ifs_compose_dialog (drawable_id))
418 return;
419 break;
420
421 case GIMP_RUN_NONINTERACTIVE:
422 status = GIMP_PDB_CALLING_ERROR;
423 break;
424
425 case GIMP_RUN_WITH_LAST_VALS:
426 {
427 gint length = gimp_get_data_size (PLUG_IN_PROC);
428
429 if (length > 0)
430 {
431 gchar *data = g_new (gchar, length);
432
433 gimp_get_data (PLUG_IN_PROC, data);
434 ifsvals_parse_string (data, &ifsvals, &elements);
435 g_free (data);
436 }
437 else
438 {
439 ifs_compose_set_defaults ();
440 }
441 }
442 break;
443
444 default:
445 break;
446 }
447
448 /* Render the fractal */
449 if (status == GIMP_PDB_SUCCESS)
450 {
451 if (run_mode == GIMP_RUN_INTERACTIVE)
452 {
453 gchar *str;
454 GimpParasite *parasite;
455
456 gimp_image_undo_group_start (image_id);
457
458 /* run the effect */
459 ifs_compose (drawable_id);
460
461 /* Store data for next invocation - both globally and
462 * as a parasite on this layer
463 */
464 str = ifsvals_stringify (&ifsvals, elements);
465
466 gimp_set_data (PLUG_IN_PROC, str, strlen (str) + 1);
467
468 parasite = gimp_parasite_new (PLUG_IN_PARASITE,
469 GIMP_PARASITE_PERSISTENT |
470 GIMP_PARASITE_UNDOABLE,
471 strlen (str) + 1, str);
472 gimp_item_attach_parasite (drawable_id, parasite);
473 gimp_parasite_free (parasite);
474
475 g_free (str);
476
477 gimp_image_undo_group_end (image_id);
478
479 gimp_displays_flush ();
480 }
481 else
482 {
483 /* run the effect */
484 ifs_compose (drawable_id);
485 }
486 }
487
488 values[0].data.d_status = status;
489 }
490
491 static GtkWidget *
ifs_compose_trans_page(void)492 ifs_compose_trans_page (void)
493 {
494 GtkWidget *vbox;
495 GtkWidget *table;
496 GtkWidget *label;
497
498 vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
499 gtk_container_set_border_width (GTK_CONTAINER (vbox), 12);
500
501 table = gtk_table_new (3, 6, FALSE);
502 gtk_table_set_col_spacings (GTK_TABLE (table), 12);
503 gtk_table_set_col_spacing (GTK_TABLE (table), 2, 6);
504 gtk_table_set_col_spacing (GTK_TABLE (table), 4, 6);
505 gtk_table_set_row_spacings (GTK_TABLE (table), 12);
506 gtk_table_set_row_spacing (GTK_TABLE (table), 2, 6);
507 gtk_box_pack_start (GTK_BOX (vbox), table, FALSE, FALSE, 0);
508 gtk_widget_show (table);
509
510 /* X */
511
512 label = gtk_label_new (_("X:"));
513 gtk_label_set_xalign (GTK_LABEL (label), 0.0);
514 gtk_table_attach (GTK_TABLE (table), label, 0, 1, 0, 1,
515 GTK_FILL, GTK_FILL, 0, 0);
516 gtk_widget_show (label);
517
518 ifsD->x_pair = value_pair_create (&ifsD->current_vals.x, 0.0, 1.0, FALSE,
519 VALUE_PAIR_DOUBLE);
520 gtk_table_attach (GTK_TABLE (table), ifsD->x_pair->spin, 1, 2, 0, 1,
521 GTK_FILL, GTK_FILL, 0, 0);
522 gtk_widget_show (ifsD->x_pair->spin);
523
524 /* Y */
525
526 label = gtk_label_new (_("Y:"));
527 gtk_label_set_xalign (GTK_LABEL (label), 0.0);
528 gtk_table_attach (GTK_TABLE (table), label, 0, 1, 1, 2,
529 GTK_FILL, GTK_FILL, 0, 0);
530 gtk_widget_show (label);
531
532 ifsD->y_pair = value_pair_create (&ifsD->current_vals.y, 0.0, 1.0, FALSE,
533 VALUE_PAIR_DOUBLE);
534 gtk_table_attach (GTK_TABLE (table), ifsD->y_pair->spin, 1, 2, 1, 2,
535 GTK_FILL, GTK_FILL, 0, 0);
536 gtk_widget_show (ifsD->y_pair->spin);
537
538 /* Scale */
539
540 label = gtk_label_new (_("Scale:"));
541 gtk_label_set_xalign (GTK_LABEL (label), 0.0);
542 gtk_table_attach (GTK_TABLE (table), label, 2, 3, 0, 1,
543 GTK_FILL, GTK_FILL, 0, 0);
544 gtk_widget_show (label);
545
546 ifsD->scale_pair = value_pair_create (&ifsD->current_vals.scale, 0.0, 1.0,
547 FALSE, VALUE_PAIR_DOUBLE);
548 gtk_table_attach (GTK_TABLE (table), ifsD->scale_pair->spin, 3, 4, 0, 1,
549 GTK_FILL, GTK_FILL, 0, 0);
550 gtk_widget_show (ifsD->scale_pair->spin);
551
552 /* Angle */
553
554 label = gtk_label_new (_("Angle:"));
555 gtk_label_set_xalign (GTK_LABEL (label), 0.0);
556 gtk_table_attach (GTK_TABLE (table), label, 2, 3, 1, 2,
557 GTK_FILL, GTK_FILL, 0, 0);
558 gtk_widget_show (label);
559
560 ifsD->angle_pair = value_pair_create (&ifsD->current_vals.theta, -180, 180,
561 FALSE, VALUE_PAIR_DOUBLE);
562 gtk_table_attach (GTK_TABLE (table), ifsD->angle_pair->spin, 3, 4, 1, 2,
563 GTK_FILL, GTK_FILL, 0, 0);
564 gtk_widget_show (ifsD->angle_pair->spin);
565
566 /* Asym */
567
568 label = gtk_label_new (_("Asymmetry:"));
569 gtk_label_set_xalign (GTK_LABEL (label), 0.0);
570 gtk_table_attach (GTK_TABLE (table), label, 4, 5, 0, 1,
571 GTK_FILL, GTK_FILL, 0, 0);
572 gtk_widget_show (label);
573
574 ifsD->asym_pair = value_pair_create (&ifsD->current_vals.asym, 0.10, 10.0,
575 FALSE, VALUE_PAIR_DOUBLE);
576 gtk_table_attach (GTK_TABLE (table), ifsD->asym_pair->spin, 5, 6, 0, 1,
577 GTK_FILL, GTK_FILL, 0, 0);
578 gtk_widget_show (ifsD->asym_pair->spin);
579
580 /* Shear */
581
582 label = gtk_label_new (_("Shear:"));
583 gtk_label_set_xalign (GTK_LABEL (label), 0.0);
584 gtk_table_attach (GTK_TABLE (table), label, 4, 5, 1, 2,
585 GTK_FILL, GTK_FILL, 0, 0);
586 gtk_widget_show (label);
587
588 ifsD->shear_pair = value_pair_create (&ifsD->current_vals.shear, -10.0, 10.0,
589 FALSE, VALUE_PAIR_DOUBLE);
590 gtk_table_attach (GTK_TABLE (table), ifsD->shear_pair->spin, 5, 6, 1, 2,
591 GTK_FILL, GTK_FILL, 0, 0);
592 gtk_widget_show (ifsD->shear_pair->spin);
593
594 /* Flip */
595
596 ifsD->flip_check_button = gtk_check_button_new_with_label (_("Flip"));
597 gtk_table_attach (GTK_TABLE (table), ifsD->flip_check_button, 0, 6, 2, 3,
598 GTK_FILL, GTK_FILL, 0, 0);
599 g_signal_connect (ifsD->flip_check_button, "toggled",
600 G_CALLBACK (flip_check_button_callback),
601 NULL);
602 gtk_widget_show (ifsD->flip_check_button);
603
604 return vbox;
605 }
606
607 static GtkWidget *
ifs_compose_color_page(void)608 ifs_compose_color_page (void)
609 {
610 GtkWidget *vbox;
611 GtkWidget *table;
612 GtkWidget *label;
613 GSList *group = NULL;
614 GimpRGB color;
615
616 vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
617 gtk_container_set_border_width (GTK_CONTAINER (vbox), 12);
618
619 table = gtk_table_new (3, 5, FALSE);
620 gtk_table_set_col_spacings (GTK_TABLE (table), 12);
621 gtk_table_set_row_spacings (GTK_TABLE (table), 6);
622 gtk_box_pack_start (GTK_BOX (vbox), table, FALSE, FALSE, 0);
623 gtk_widget_show (table);
624
625 /* Simple color control section */
626
627 ifsD->simple_button = gtk_radio_button_new_with_label (group, _("Simple"));
628 gtk_table_attach (GTK_TABLE (table), ifsD->simple_button, 0, 1, 0, 2,
629 GTK_FILL, GTK_FILL, 0, 0);
630 group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (ifsD->simple_button));
631 g_signal_connect (ifsD->simple_button, "toggled",
632 G_CALLBACK (simple_color_toggled),
633 NULL);
634 gtk_widget_show (ifsD->simple_button);
635
636 ifsD->target_cmap = color_map_create (_("IFS Fractal: Target"), NULL,
637 &ifsD->current_vals.target_color, TRUE);
638 gtk_table_attach (GTK_TABLE (table), ifsD->target_cmap->hbox, 1, 2, 0, 2,
639 GTK_FILL, 0, 0, 0);
640 gtk_widget_show (ifsD->target_cmap->hbox);
641
642 label = gtk_label_new (_("Scale hue by:"));
643 gtk_label_set_xalign (GTK_LABEL (label), 0.0);
644 gtk_table_attach (GTK_TABLE (table), label, 2, 3, 0, 1,
645 GTK_FILL, GTK_FILL, 0, 0);
646 gtk_widget_show (label);
647
648 ifsD->hue_scale_pair = value_pair_create (&ifsD->current_vals.hue_scale,
649 0.0, 1.0, TRUE, VALUE_PAIR_DOUBLE);
650 gtk_table_attach (GTK_TABLE (table), ifsD->hue_scale_pair->scale, 3, 4, 0, 1,
651 GTK_FILL, GTK_FILL, 0, 0);
652 gtk_widget_show (ifsD->hue_scale_pair->scale);
653 gtk_table_attach (GTK_TABLE (table), ifsD->hue_scale_pair->spin, 4, 5, 0, 1,
654 GTK_FILL, GTK_FILL, 0, 0);
655 gtk_widget_show (ifsD->hue_scale_pair->spin);
656
657 label = gtk_label_new (_("Scale value by:"));
658 gtk_label_set_xalign (GTK_LABEL (label), 0.0);
659 gtk_table_attach (GTK_TABLE (table), label, 2, 3, 1, 2,
660 GTK_FILL, GTK_FILL, 0, 0);
661 gtk_widget_show (label);
662
663 ifsD->value_scale_pair = value_pair_create (&ifsD->current_vals.value_scale,
664 0.0, 1.0, TRUE, VALUE_PAIR_DOUBLE);
665 gtk_table_attach (GTK_TABLE (table), ifsD->value_scale_pair->scale,
666 3, 4, 1, 2, GTK_FILL, GTK_FILL, 0, 0);
667 gtk_widget_show (ifsD->value_scale_pair->scale);
668 gtk_table_attach (GTK_TABLE (table), ifsD->value_scale_pair->spin,
669 4, 5, 1, 2, GTK_FILL, GTK_FILL, 0, 0);
670 gtk_widget_show (ifsD->value_scale_pair->spin);
671
672 /* Full color control section */
673
674 ifsD->full_button = gtk_radio_button_new_with_label (group, _("Full"));
675 gtk_table_attach (GTK_TABLE (table), ifsD->full_button, 0, 1, 2, 3,
676 GTK_FILL, GTK_FILL, 0, 0);
677 group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (ifsD->full_button));
678 gtk_widget_show (ifsD->full_button);
679
680 gimp_rgb_parse_name (&color, "red", -1);
681 gimp_rgb_set_alpha (&color, 1.0);
682 ifsD->red_cmap = color_map_create (_("IFS Fractal: Red"), &color,
683 &ifsD->current_vals.red_color, FALSE);
684 gtk_table_attach (GTK_TABLE (table), ifsD->red_cmap->hbox, 1, 2, 2, 3,
685 GTK_FILL, GTK_FILL, 0, 0);
686 gtk_widget_show (ifsD->red_cmap->hbox);
687
688 gimp_rgb_parse_name (&color, "green", -1);
689 gimp_rgb_set_alpha (&color, 1.0);
690 ifsD->green_cmap = color_map_create (_("IFS Fractal: Green"), &color,
691 &ifsD->current_vals.green_color, FALSE);
692 gtk_table_attach (GTK_TABLE (table), ifsD->green_cmap->hbox, 2, 3, 2, 3,
693 GTK_FILL, GTK_FILL, 0, 0);
694 gtk_widget_show (ifsD->green_cmap->hbox);
695
696 gimp_rgb_parse_name (&color, "blue", -1);
697 gimp_rgb_set_alpha (&color, 1.0);
698 ifsD->blue_cmap = color_map_create (_("IFS Fractal: Blue"), &color,
699 &ifsD->current_vals.blue_color, FALSE);
700 gtk_table_attach (GTK_TABLE (table), ifsD->blue_cmap->hbox, 3, 4, 2, 3,
701 GTK_FILL, GTK_FILL, 0, 0);
702 gtk_widget_show (ifsD->blue_cmap->hbox);
703
704 gimp_rgb_parse_name (&color, "black", -1);
705 gimp_rgb_set_alpha (&color, 1.0);
706 ifsD->black_cmap = color_map_create (_("IFS Fractal: Black"), &color,
707 &ifsD->current_vals.black_color, FALSE);
708 gtk_table_attach (GTK_TABLE (table), ifsD->black_cmap->hbox, 4, 5, 2, 3,
709 GTK_FILL, GTK_FILL, 0, 0);
710 gtk_widget_show (ifsD->black_cmap->hbox);
711
712 return vbox;
713 }
714
715 static gint
ifs_compose_dialog(gint32 drawable_id)716 ifs_compose_dialog (gint32 drawable_id)
717 {
718 GtkWidget *dialog;
719 GtkWidget *label;
720 GtkWidget *vbox;
721 GtkWidget *hbox;
722 GtkWidget *main_vbox;
723 GtkWidget *toolbar;
724 GtkWidget *aspect_frame;
725 GtkWidget *notebook;
726 GtkWidget *page;
727 gint design_width = gimp_drawable_width (drawable_id);
728 gint design_height = gimp_drawable_height (drawable_id);
729
730 if (design_width > design_height)
731 {
732 if (design_width > DESIGN_AREA_MAX_SIZE)
733 {
734 design_height = design_height * DESIGN_AREA_MAX_SIZE / design_width;
735 design_width = DESIGN_AREA_MAX_SIZE;
736 }
737 }
738 else
739 {
740 if (design_height > DESIGN_AREA_MAX_SIZE)
741 {
742 design_width = design_width * DESIGN_AREA_MAX_SIZE / design_height;
743 design_height = DESIGN_AREA_MAX_SIZE;
744 }
745 }
746
747 ifsD = g_new0 (IfsDialog, 1);
748
749 ifsD->drawable_width = gimp_drawable_width (drawable_id);
750 ifsD->drawable_height = gimp_drawable_height (drawable_id);
751 ifsD->preview_width = design_width;
752 ifsD->preview_height = design_height;
753
754 gimp_ui_init (PLUG_IN_BINARY, TRUE);
755
756 dialog = gimp_dialog_new (_("IFS Fractal"), PLUG_IN_ROLE,
757 NULL, 0,
758 gimp_standard_help_func, PLUG_IN_PROC,
759
760 _("_Open"), RESPONSE_OPEN,
761 _("_Save"), RESPONSE_SAVE,
762 _("_Reset"), RESPONSE_RESET,
763 _("_Cancel"), GTK_RESPONSE_CANCEL,
764 _("_OK"), GTK_RESPONSE_OK,
765
766 NULL);
767
768 gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
769 RESPONSE_OPEN,
770 RESPONSE_SAVE,
771 RESPONSE_RESET,
772 GTK_RESPONSE_OK,
773 GTK_RESPONSE_CANCEL,
774 -1);
775
776 gimp_window_set_transient (GTK_WINDOW (dialog));
777
778 g_object_add_weak_pointer (G_OBJECT (dialog), (gpointer) &dialog);
779
780 g_signal_connect (dialog, "response",
781 G_CALLBACK (ifs_compose_response),
782 NULL);
783 g_signal_connect (dialog, "destroy",
784 G_CALLBACK (gtk_main_quit),
785 NULL);
786
787 design_area_create (dialog, design_width, design_height);
788
789 toolbar = gtk_ui_manager_get_widget (ifsDesign->ui_manager,
790 "/ifs-compose-toolbar");
791 gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))),
792 toolbar, FALSE, FALSE, 0);
793 gtk_widget_show (toolbar);
794
795 /* The main vbox */
796 main_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
797 gtk_container_set_border_width (GTK_CONTAINER (main_vbox), 12);
798 gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))),
799 main_vbox, TRUE, TRUE, 0);
800
801 /* The design area */
802
803 hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
804 gtk_box_pack_start (GTK_BOX (main_vbox), hbox, FALSE, FALSE, 0);
805
806 aspect_frame = gtk_aspect_frame_new (NULL,
807 0.5, 0.5,
808 (gdouble) design_width / design_height,
809 0);
810 gtk_frame_set_shadow_type (GTK_FRAME (aspect_frame), GTK_SHADOW_IN);
811 gtk_box_pack_start (GTK_BOX (hbox), aspect_frame, TRUE, TRUE, 0);
812 gtk_widget_show (aspect_frame);
813
814 gtk_container_add (GTK_CONTAINER (aspect_frame), ifsDesign->area);
815 gtk_widget_show (ifsDesign->area);
816
817 /* The Preview */
818
819 aspect_frame = gtk_aspect_frame_new (NULL,
820 0.5, 0.5,
821 (gdouble) design_width / design_height,
822 0);
823 gtk_frame_set_shadow_type (GTK_FRAME (aspect_frame), GTK_SHADOW_IN);
824 gtk_box_pack_start (GTK_BOX (hbox), aspect_frame, TRUE, TRUE, 0);
825
826 ifsD->preview = gimp_preview_area_new ();
827 gtk_widget_set_size_request (ifsD->preview,
828 ifsD->preview_width,
829 ifsD->preview_height);
830 gtk_container_add (GTK_CONTAINER (aspect_frame), ifsD->preview);
831 gtk_widget_show (ifsD->preview);
832
833 gtk_widget_show (aspect_frame);
834
835 gtk_widget_show (hbox);
836
837 /* The current transformation frame */
838
839 ifsD->current_frame = gimp_frame_new (NULL);
840 gtk_box_pack_start (GTK_BOX (main_vbox), ifsD->current_frame,
841 FALSE, FALSE, 0);
842
843 vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
844 gtk_container_add (GTK_CONTAINER (ifsD->current_frame), vbox);
845
846 /* The notebook */
847
848 notebook = gtk_notebook_new ();
849 gtk_notebook_set_tab_pos (GTK_NOTEBOOK (notebook), GTK_POS_TOP);
850 gtk_box_pack_start (GTK_BOX (vbox), notebook, FALSE, FALSE, 0);
851 gtk_widget_show (notebook);
852
853 page = ifs_compose_trans_page ();
854 label = gtk_label_new (_("Spatial Transformation"));
855 gtk_label_set_xalign (GTK_LABEL (label), 0.5);
856 gtk_notebook_append_page (GTK_NOTEBOOK (notebook), page, label);
857 gtk_widget_show (page);
858
859 page = ifs_compose_color_page ();
860 label = gtk_label_new (_("Color Transformation"));
861 gtk_label_set_xalign (GTK_LABEL (label), 0.5);
862 gtk_notebook_append_page (GTK_NOTEBOOK (notebook), page, label);
863 gtk_widget_show (page);
864
865 /* The probability entry */
866
867 hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
868 gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
869
870 label = gtk_label_new (_("Relative probability:"));
871 gtk_label_set_xalign (GTK_LABEL (label), 0.0);
872 gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
873 gtk_widget_show (label);
874
875 ifsD->prob_pair = value_pair_create (&ifsD->current_vals.prob, 0.0, 5.0, TRUE,
876 VALUE_PAIR_DOUBLE);
877 gtk_box_pack_start (GTK_BOX (hbox), ifsD->prob_pair->scale, TRUE, TRUE, 0);
878 gtk_widget_show (ifsD->prob_pair->scale);
879 gtk_box_pack_start (GTK_BOX (hbox), ifsD->prob_pair->spin, FALSE, TRUE, 0);
880 gtk_widget_show (ifsD->prob_pair->spin);
881
882 gtk_widget_show (hbox);
883 gtk_widget_show (vbox);
884 gtk_widget_show (ifsD->current_frame);
885
886 gtk_widget_show (main_vbox);
887
888 if (ifsvals.num_elements == 0)
889 {
890 ifs_compose_set_defaults ();
891 }
892 else
893 {
894 gint i;
895 gdouble ratio = (gdouble) ifsD->drawable_height / ifsD->drawable_width;
896
897 element_selected = g_new (gint, ifsvals.num_elements);
898 element_selected[0] = TRUE;
899 for (i = 1; i < ifsvals.num_elements; i++)
900 element_selected[i] = FALSE;
901
902 if (ratio != ifsvals.aspect_ratio)
903 {
904 /* Adjust things so that what fit onto the old image, fits
905 onto the new image */
906 Aff2 t1, t2, t3;
907 gdouble x_offset, y_offset;
908 gdouble center_x, center_y;
909 gdouble scale;
910
911 if (ratio < ifsvals.aspect_ratio)
912 {
913 scale = ratio/ifsvals.aspect_ratio;
914 x_offset = (1-scale)/2;
915 y_offset = 0;
916 }
917 else
918 {
919 scale = 1;
920 x_offset = 0;
921 y_offset = (ratio - ifsvals.aspect_ratio)/2;
922 }
923 aff2_scale (&t1, scale, 0);
924 aff2_translate (&t2, x_offset, y_offset);
925 aff2_compose (&t3, &t2, &t1);
926 aff2_invert (&t1, &t3);
927
928 aff2_apply (&t3, ifsvals.center_x, ifsvals.center_y, ¢er_x,
929 ¢er_y);
930
931 for (i = 0; i < ifsvals.num_elements; i++)
932 {
933 aff_element_compute_trans (elements[i],1, ifsvals.aspect_ratio,
934 ifsvals.center_x, ifsvals.center_y);
935 aff2_compose (&t2, &elements[i]->trans, &t1);
936 aff2_compose (&elements[i]->trans, &t3, &t2);
937 aff_element_decompose_trans (elements[i],&elements[i]->trans,
938 1, ifsvals.aspect_ratio,
939 center_x, center_y);
940 }
941 ifsvals.center_x = center_x;
942 ifsvals.center_y = center_y;
943
944 ifsvals.aspect_ratio = ratio;
945 }
946
947 for (i = 0; i < ifsvals.num_elements; i++)
948 aff_element_compute_color_trans (elements[i]);
949 /* boundary and spatial transformations will be computed
950 when the design_area gets a ConfigureNotify event */
951
952 set_current_element (0);
953
954 ifsD->selected_orig = g_new (AffElement, ifsvals.num_elements);
955 }
956
957 gtk_widget_show (dialog);
958
959 ifs_compose_preview ();
960
961 gtk_main ();
962
963 g_object_unref (ifsDesign->ui_manager);
964
965 if (dialog)
966 gtk_widget_destroy (dialog);
967
968 if (ifsOptD)
969 gtk_widget_destroy (ifsOptD->dialog);
970
971 gdk_flush ();
972
973 g_free (ifsD);
974
975 return ifscint.run;
976 }
977
978 static void
design_area_create(GtkWidget * window,gint design_width,gint design_height)979 design_area_create (GtkWidget *window,
980 gint design_width,
981 gint design_height)
982 {
983 ifsDesign = g_new0 (IfsDesignArea, 1);
984
985 ifsDesign->op = OP_TRANSLATE;
986
987 ifsDesign->area = gtk_drawing_area_new ();
988 gtk_widget_set_size_request (ifsDesign->area, design_width, design_height);
989
990 g_signal_connect (ifsDesign->area, "realize",
991 G_CALLBACK (design_area_realize),
992 NULL);
993 g_signal_connect (ifsDesign->area, "expose-event",
994 G_CALLBACK (design_area_expose),
995 NULL);
996 g_signal_connect (ifsDesign->area, "button-press-event",
997 G_CALLBACK (design_area_button_press),
998 NULL);
999 g_signal_connect (ifsDesign->area, "button-release-event",
1000 G_CALLBACK (design_area_button_release),
1001 NULL);
1002 g_signal_connect (ifsDesign->area, "motion-notify-event",
1003 G_CALLBACK (design_area_motion),
1004 NULL);
1005 g_signal_connect (ifsDesign->area, "configure-event",
1006 G_CALLBACK (design_area_configure),
1007 NULL);
1008 gtk_widget_set_events (ifsDesign->area,
1009 GDK_EXPOSURE_MASK |
1010 GDK_BUTTON_PRESS_MASK |
1011 GDK_BUTTON_RELEASE_MASK |
1012 GDK_POINTER_MOTION_MASK |
1013 GDK_POINTER_MOTION_HINT_MASK);
1014
1015 ifsDesign->ui_manager = design_op_menu_create (window);
1016 design_op_actions_update ();
1017 }
1018
1019 static GtkUIManager *
design_op_menu_create(GtkWidget * window)1020 design_op_menu_create (GtkWidget *window)
1021 {
1022 static GtkActionEntry actions[] =
1023 {
1024 { "ifs-compose-menu", NULL, "IFS Fractal Menu" },
1025
1026 { "new", GIMP_ICON_DOCUMENT_NEW,
1027 N_("_New"), "<primary>N", NULL,
1028 G_CALLBACK (ifs_compose_new_callback) },
1029
1030 { "delete", GIMP_ICON_EDIT_DELETE,
1031 N_("_Delete"), "<primary>D", NULL,
1032 G_CALLBACK (ifs_compose_delete_callback) },
1033
1034 { "undo", GIMP_ICON_EDIT_UNDO,
1035 N_("_Undo"), "<primary>Z", NULL,
1036 G_CALLBACK (undo) },
1037
1038 { "redo", GIMP_ICON_EDIT_REDO,
1039 N_("_Redo"), "<primary>Y", NULL,
1040 G_CALLBACK (redo) },
1041
1042 { "select-all", GIMP_ICON_SELECTION_ALL,
1043 N_("Select _All"), "<primary>A", NULL,
1044 G_CALLBACK (design_area_select_all_callback) },
1045
1046 { "center", GIMP_ICON_CENTER,
1047 N_("Re_center"), "<primary>C", N_("Recompute Center"),
1048 G_CALLBACK (recompute_center_cb) },
1049
1050 { "options", GIMP_ICON_PREFERENCES_SYSTEM,
1051 N_("Render Options"), NULL, NULL,
1052 G_CALLBACK (ifs_compose_options_callback) }
1053 };
1054 static GtkRadioActionEntry radio_actions[] =
1055 {
1056 { "move", GIMP_ICON_TOOL_MOVE,
1057 N_("Move"), "M", NULL, OP_TRANSLATE },
1058
1059 { "rotate", GIMP_ICON_TOOL_ROTATE,
1060 N_("Rotate"), "R", N_("Rotate / Scale"), OP_ROTATE },
1061
1062 { "stretch", GIMP_ICON_TOOL_PERSPECTIVE,
1063 N_("Stretch"), "S", NULL, OP_STRETCH }
1064 };
1065
1066 GtkUIManager *ui_manager = gtk_ui_manager_new ();
1067 GtkActionGroup *group = gtk_action_group_new ("Actions");
1068
1069 gtk_action_group_set_translation_domain (group, NULL);
1070
1071 gtk_action_group_add_actions (group,
1072 actions,
1073 G_N_ELEMENTS (actions),
1074 window);
1075 gtk_action_group_add_radio_actions (group,
1076 radio_actions,
1077 G_N_ELEMENTS (radio_actions),
1078 ifsDesign->op,
1079 G_CALLBACK (design_op_update_callback),
1080 window);
1081
1082 gtk_window_add_accel_group (GTK_WINDOW (window),
1083 gtk_ui_manager_get_accel_group (ui_manager));
1084 gtk_accel_group_lock (gtk_ui_manager_get_accel_group (ui_manager));
1085
1086 gtk_ui_manager_insert_action_group (ui_manager, group, -1);
1087 g_object_unref (group);
1088
1089 gtk_ui_manager_add_ui_from_string (ui_manager,
1090 "<ui>"
1091 " <menubar name=\"dummy-menubar\">"
1092 " <menu action=\"ifs-compose-menu\">"
1093 " <menuitem action=\"move\" />"
1094 " <menuitem action=\"rotate\" />"
1095 " <menuitem action=\"stretch\" />"
1096 " <separator />"
1097 " <menuitem action=\"new\" />"
1098 " <menuitem action=\"delete\" />"
1099 " <menuitem action=\"undo\" />"
1100 " <menuitem action=\"redo\" />"
1101 " <menuitem action=\"select-all\" />"
1102 " <menuitem action=\"center\" />"
1103 " <separator />"
1104 " <menuitem action=\"options\" />"
1105 " </menu>"
1106 " </menubar>"
1107 "</ui>",
1108 -1, NULL);
1109
1110 gtk_ui_manager_add_ui_from_string (ui_manager,
1111 "<ui>"
1112 " <toolbar name=\"ifs-compose-toolbar\">"
1113 " <toolitem action=\"move\" />"
1114 " <toolitem action=\"rotate\" />"
1115 " <toolitem action=\"stretch\" />"
1116 " <separator />"
1117 " <toolitem action=\"new\" />"
1118 " <toolitem action=\"delete\" />"
1119 " <toolitem action=\"undo\" />"
1120 " <toolitem action=\"redo\" />"
1121 " <toolitem action=\"select-all\" />"
1122 " <toolitem action=\"center\" />"
1123 " <separator />"
1124 " <toolitem action=\"options\" />"
1125 " </toolbar>"
1126 "</ui>",
1127 -1, NULL);
1128
1129 return ui_manager;
1130 }
1131
1132 static void
design_op_actions_update(void)1133 design_op_actions_update (void)
1134 {
1135 GtkAction *act;
1136
1137 act = gtk_ui_manager_get_action (ifsDesign->ui_manager,
1138 "/ui/dummy-menubar/ifs-compose-menu/undo");
1139 gtk_action_set_sensitive (act, undo_cur >= 0);
1140
1141 act = gtk_ui_manager_get_action (ifsDesign->ui_manager,
1142 "/ui/dummy-menubar/ifs-compose-menu/redo");
1143 gtk_action_set_sensitive (act, undo_cur != undo_num - 1);
1144
1145 act = gtk_ui_manager_get_action (ifsDesign->ui_manager,
1146 "/ui/dummy-menubar/ifs-compose-menu/delete");
1147 gtk_action_set_sensitive (act, ifsvals.num_elements > 2);
1148 }
1149
1150 static void
ifs_options_dialog(GtkWidget * parent)1151 ifs_options_dialog (GtkWidget *parent)
1152 {
1153 if (!ifsOptD)
1154 {
1155 GtkWidget *table;
1156 GtkWidget *label;
1157
1158 ifsOptD = g_new0 (IfsOptionsDialog, 1);
1159
1160 ifsOptD->dialog =
1161 gimp_dialog_new (_("IFS Fractal Render Options"), PLUG_IN_ROLE,
1162 parent, 0,
1163 gimp_standard_help_func, PLUG_IN_PROC,
1164
1165 _("_Close"), GTK_RESPONSE_CLOSE,
1166
1167 NULL);
1168
1169 g_signal_connect (ifsOptD->dialog, "response",
1170 G_CALLBACK (gtk_widget_hide),
1171 NULL);
1172
1173 /* Table of options */
1174
1175 table = gtk_table_new (4, 3, FALSE);
1176 gtk_container_set_border_width (GTK_CONTAINER (table), 12);
1177 gtk_table_set_row_spacings (GTK_TABLE (table), 6);
1178 gtk_table_set_col_spacings (GTK_TABLE (table), 6);
1179 gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (ifsOptD->dialog))),
1180 table, FALSE, FALSE, 0);
1181 gtk_widget_show (table);
1182
1183 label = gtk_label_new (_("Max. memory:"));
1184 gtk_label_set_xalign (GTK_LABEL (label), 0.0);
1185 gtk_table_attach (GTK_TABLE (table), label, 0, 1, 0, 1,
1186 GTK_FILL, GTK_FILL, 0, 0);
1187 gtk_widget_show (label);
1188
1189 ifsOptD->memory_pair = value_pair_create (&ifsvals.max_memory,
1190 1, 1000000, FALSE,
1191 VALUE_PAIR_INT);
1192 gtk_table_attach (GTK_TABLE (table), ifsOptD->memory_pair->spin,
1193 1, 2, 0, 1, GTK_FILL, GTK_FILL, 0, 0);
1194 gtk_widget_show (ifsOptD->memory_pair->spin);
1195
1196 label = gtk_label_new (_("Iterations:"));
1197 gtk_label_set_xalign (GTK_LABEL (label), 0.0);
1198 gtk_table_attach (GTK_TABLE (table), label, 0, 1, 1, 2,
1199 GTK_FILL, GTK_FILL, 0, 0);
1200 gtk_widget_show (label);
1201
1202 ifsOptD->iterations_pair = value_pair_create (&ifsvals.iterations,
1203 1, 10000000, FALSE,
1204 VALUE_PAIR_INT);
1205 gtk_table_attach (GTK_TABLE (table), ifsOptD->iterations_pair->spin,
1206 1, 2, 1, 2, GTK_FILL, GTK_FILL, 0, 0);
1207 gtk_widget_show (ifsOptD->iterations_pair->spin);
1208 gtk_widget_show (label);
1209
1210 label = gtk_label_new (_("Subdivide:"));
1211 gtk_label_set_xalign (GTK_LABEL (label), 0.0);
1212 gtk_table_attach (GTK_TABLE (table), label, 0, 1, 2, 3,
1213 GTK_FILL, GTK_FILL, 0, 0);
1214 gtk_widget_show (label);
1215
1216 ifsOptD->subdivide_pair = value_pair_create (&ifsvals.subdivide,
1217 1, 10, FALSE,
1218 VALUE_PAIR_INT);
1219 gtk_table_attach (GTK_TABLE (table), ifsOptD->subdivide_pair->spin,
1220 1, 2, 2, 3, GTK_FILL, GTK_FILL, 0, 0);
1221 gtk_widget_show (ifsOptD->subdivide_pair->spin);
1222
1223 label = gtk_label_new (_("Spot radius:"));
1224 gtk_label_set_xalign (GTK_LABEL (label), 0.0);
1225 gtk_table_attach (GTK_TABLE (table), label, 0, 1, 3, 4,
1226 GTK_FILL, GTK_FILL, 0, 0);
1227 gtk_widget_show (label);
1228
1229 ifsOptD->radius_pair = value_pair_create (&ifsvals.radius,
1230 0, 5, TRUE,
1231 VALUE_PAIR_DOUBLE);
1232 gtk_table_attach (GTK_TABLE (table), ifsOptD->radius_pair->scale,
1233 1, 2, 3, 4, GTK_FILL, GTK_FILL, 0, 0);
1234 gtk_widget_show (ifsOptD->radius_pair->scale);
1235 gtk_table_attach (GTK_TABLE (table), ifsOptD->radius_pair->spin,
1236 2, 3, 3, 4, GTK_FILL, GTK_FILL, 0, 0);
1237 gtk_widget_show (ifsOptD->radius_pair->spin);
1238
1239 value_pair_update (ifsOptD->iterations_pair);
1240 value_pair_update (ifsOptD->subdivide_pair);
1241 value_pair_update (ifsOptD->memory_pair);
1242 value_pair_update (ifsOptD->radius_pair);
1243
1244 gtk_widget_show (ifsOptD->dialog);
1245 }
1246 else
1247 {
1248 gtk_window_present (GTK_WINDOW (ifsOptD->dialog));
1249 }
1250 }
1251
1252 static void
ifs_compose(gint32 drawable_id)1253 ifs_compose (gint32 drawable_id)
1254 {
1255 GeglBuffer *buffer = gimp_drawable_get_shadow_buffer (drawable_id);
1256 gint width = gimp_drawable_width (drawable_id);
1257 gint height = gimp_drawable_height (drawable_id);
1258 gboolean alpha = gimp_drawable_has_alpha (drawable_id);
1259 const Babl *format;
1260 gint num_bands;
1261 gint band_height;
1262 gint band_y;
1263 gint band_no;
1264 gint i, j;
1265 guchar *data;
1266 guchar *mask = NULL;
1267 guchar *nhits;
1268 guchar rc, gc, bc;
1269 GimpRGB color;
1270
1271 if (alpha)
1272 format = babl_format ("R'G'B'A u8");
1273 else
1274 format = babl_format ("R'G'B' u8");
1275
1276 num_bands = ceil ((gdouble) (width * height * SQR (ifsvals.subdivide) * 5)
1277 / (1024 * ifsvals.max_memory));
1278 band_height = (height + num_bands - 1) / num_bands;
1279
1280 if (band_height > height)
1281 band_height = height;
1282
1283 mask = g_new (guchar, width * band_height * SQR (ifsvals.subdivide));
1284 data = g_new (guchar, width * band_height * SQR (ifsvals.subdivide) * 3);
1285 nhits = g_new (guchar, width * band_height * SQR (ifsvals.subdivide));
1286
1287 gimp_context_get_background (&color);
1288 gimp_rgb_get_uchar (&color, &rc, &gc, &bc);
1289
1290 for (band_no = 0, band_y = 0; band_no < num_bands; band_no++)
1291 {
1292 GeglBufferIterator *iter;
1293 GeglRectangle *roi;
1294
1295 gimp_progress_init_printf (_("Rendering IFS (%d/%d)"),
1296 band_no + 1, num_bands);
1297
1298 /* render the band to a buffer */
1299 if (band_y + band_height > height)
1300 band_height = height - band_y;
1301
1302 /* we don't need to clear data since we store nhits */
1303 memset (mask, 0, width * band_height * SQR (ifsvals.subdivide));
1304 memset (nhits, 0, width * band_height * SQR (ifsvals.subdivide));
1305
1306 ifs_render (elements,
1307 ifsvals.num_elements, width, height, ifsvals.iterations,
1308 &ifsvals, band_y, band_height, data, mask, nhits, FALSE);
1309
1310 /* transfer the image to the drawable */
1311
1312 iter = gegl_buffer_iterator_new (buffer,
1313 GEGL_RECTANGLE (0, band_y,
1314 width, band_height), 0,
1315 format,
1316 GEGL_ACCESS_WRITE, GEGL_ABYSS_NONE, 1);
1317 roi = &iter->items[0].roi;
1318
1319 while (gegl_buffer_iterator_next (iter))
1320 {
1321 guchar *destrow = iter->items[0].data;
1322
1323 for (j = roi->y; j < (roi->y + roi->height); j++)
1324 {
1325 guchar *dest = destrow;
1326
1327 for (i = roi->x; i < (roi->x + roi->width); i++)
1328 {
1329 /* Accumulate a reduced pixel */
1330
1331 gint rtot = 0;
1332 gint btot = 0;
1333 gint gtot = 0;
1334 gint mtot = 0;
1335 gint ii, jj;
1336
1337 for (jj = 0; jj < ifsvals.subdivide; jj++)
1338 {
1339 guchar *ptr;
1340 guchar *maskptr;
1341
1342 ptr = data +
1343 3 * (((j - band_y) * ifsvals.subdivide + jj) *
1344 ifsvals.subdivide * width +
1345 i * ifsvals.subdivide);
1346
1347 maskptr = mask +
1348 ((j - band_y) * ifsvals.subdivide + jj) *
1349 ifsvals.subdivide * width +
1350 i * ifsvals.subdivide;
1351
1352 for (ii = 0; ii < ifsvals.subdivide; ii++)
1353 {
1354 guchar maskval = *maskptr++;
1355
1356 mtot += maskval;
1357 rtot += maskval* *ptr++;
1358 gtot += maskval* *ptr++;
1359 btot += maskval* *ptr++;
1360 }
1361 }
1362
1363 if (mtot)
1364 {
1365 rtot /= mtot;
1366 gtot /= mtot;
1367 btot /= mtot;
1368 mtot /= SQR (ifsvals.subdivide);
1369 }
1370
1371 if (alpha)
1372 {
1373 *dest++ = rtot;
1374 *dest++ = gtot;
1375 *dest++ = btot;
1376 *dest++ = mtot;
1377 }
1378 else
1379 {
1380 *dest++ = (mtot * rtot + (255 - mtot) * rc) / 255;
1381 *dest++ = (mtot * gtot + (255 - mtot) * gc) / 255;
1382 *dest++ = (mtot * btot + (255 - mtot) * bc) / 255;
1383 }
1384 }
1385
1386 if (alpha)
1387 destrow += roi->width * 4;
1388 else
1389 destrow += roi->width * 3;
1390 }
1391 }
1392
1393 band_y += band_height;
1394 }
1395
1396 g_free (mask);
1397 g_free (data);
1398 g_free (nhits);
1399
1400 g_object_unref (buffer);
1401
1402 gimp_drawable_merge_shadow (drawable_id, TRUE);
1403 gimp_drawable_update (drawable_id, 0, 0, width, height);
1404 }
1405
1406 static void
update_values(void)1407 update_values (void)
1408 {
1409 ifsD->in_update = TRUE;
1410
1411 ifsD->current_vals = elements[ifsD->current_element]->v;
1412 ifsD->current_vals.theta *= 180/G_PI;
1413
1414 value_pair_update (ifsD->prob_pair);
1415 value_pair_update (ifsD->x_pair);
1416 value_pair_update (ifsD->y_pair);
1417 value_pair_update (ifsD->scale_pair);
1418 value_pair_update (ifsD->angle_pair);
1419 value_pair_update (ifsD->asym_pair);
1420 value_pair_update (ifsD->shear_pair);
1421 color_map_update (ifsD->red_cmap);
1422 color_map_update (ifsD->green_cmap);
1423 color_map_update (ifsD->blue_cmap);
1424 color_map_update (ifsD->black_cmap);
1425 color_map_update (ifsD->target_cmap);
1426 value_pair_update (ifsD->hue_scale_pair);
1427 value_pair_update (ifsD->value_scale_pair);
1428
1429 if (elements[ifsD->current_element]->v.simple_color)
1430 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (ifsD->simple_button),
1431 TRUE);
1432 else
1433 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (ifsD->full_button),
1434 TRUE);
1435
1436 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (ifsD->flip_check_button),
1437 elements[ifsD->current_element]->v.flip);
1438
1439 ifsD->in_update = FALSE;
1440
1441 simple_color_set_sensitive ();
1442 }
1443
1444 static void
set_current_element(gint index)1445 set_current_element (gint index)
1446 {
1447 gchar *frame_name = g_strdup_printf (_("Transformation %s"),
1448 elements[index]->name);
1449
1450 ifsD->current_element = index;
1451
1452 gtk_frame_set_label (GTK_FRAME (ifsD->current_frame),frame_name);
1453 g_free (frame_name);
1454
1455 update_values ();
1456 }
1457
1458 static void
design_area_realize(GtkWidget * widget)1459 design_area_realize (GtkWidget *widget)
1460 {
1461 const gint cursors[3] =
1462 {
1463 GDK_FLEUR, /* OP_TRANSLATE */
1464 GDK_EXCHANGE, /* OP_ROTATE */
1465 GDK_CROSSHAIR /* OP_SHEAR */
1466 };
1467
1468 GdkDisplay *display = gtk_widget_get_display (widget);
1469 GdkCursor *cursor = gdk_cursor_new_for_display (display,
1470 cursors[ifsDesign->op]);
1471 gdk_window_set_cursor (gtk_widget_get_window (widget), cursor);
1472 gdk_cursor_unref (cursor);
1473 }
1474
1475 static gboolean
design_area_expose(GtkWidget * widget,GdkEventExpose * event)1476 design_area_expose (GtkWidget *widget,
1477 GdkEventExpose *event)
1478 {
1479 GtkStyle *style = gtk_widget_get_style (widget);
1480 GtkStateType state = gtk_widget_get_state (widget);
1481 cairo_t *cr;
1482 GtkAllocation allocation;
1483 PangoLayout *layout;
1484 gint i;
1485 gint cx, cy;
1486
1487 gtk_widget_get_allocation (widget, &allocation);
1488
1489 cr = gdk_cairo_create (ifsDesign->pixmap);
1490
1491 gdk_cairo_set_source_color (cr, &style->bg[state]);
1492 cairo_paint (cr);
1493
1494 cairo_set_line_join (cr, CAIRO_LINE_JOIN_ROUND);
1495 cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND);
1496 cairo_translate (cr, 0.5, 0.5);
1497
1498 /* draw an indicator for the center */
1499
1500 cx = ifsvals.center_x * allocation.width;
1501 cy = ifsvals.center_y * allocation.width;
1502
1503 cairo_move_to (cr, cx - 10, cy);
1504 cairo_line_to (cr, cx + 10, cy);
1505
1506 cairo_move_to (cr, cx, cy - 10);
1507 cairo_line_to (cr, cx, cy + 10);
1508
1509 gdk_cairo_set_source_color (cr, &style->fg[state]);
1510 cairo_set_line_width (cr, 1.0);
1511 cairo_stroke (cr);
1512
1513 layout = gtk_widget_create_pango_layout (widget, NULL);
1514
1515 for (i = 0; i < ifsvals.num_elements; i++)
1516 {
1517 aff_element_draw (elements[i], element_selected[i],
1518 allocation.width,
1519 allocation.height,
1520 cr,
1521 &style->fg[state],
1522 layout);
1523 }
1524
1525 g_object_unref (layout);
1526
1527 cairo_destroy (cr);
1528
1529 cr = gdk_cairo_create (gtk_widget_get_window (widget));
1530
1531 gdk_cairo_region (cr, event->region);
1532 cairo_clip (cr);
1533
1534 gdk_cairo_set_source_pixmap (cr, ifsDesign->pixmap, 0.0, 0.0);
1535 cairo_paint (cr);
1536
1537 cairo_destroy (cr);
1538
1539 return FALSE;
1540 }
1541
1542 static gboolean
design_area_configure(GtkWidget * widget,GdkEventConfigure * event)1543 design_area_configure (GtkWidget *widget,
1544 GdkEventConfigure *event)
1545 {
1546 GtkAllocation allocation;
1547 gint i;
1548
1549 gtk_widget_get_allocation (widget, &allocation);
1550
1551 for (i = 0; i < ifsvals.num_elements; i++)
1552 aff_element_compute_trans (elements[i],
1553 allocation.width, allocation.height,
1554 ifsvals.center_x, ifsvals.center_y);
1555
1556 for (i = 0; i < ifsvals.num_elements; i++)
1557 aff_element_compute_boundary (elements[i],
1558 allocation.width, allocation.height,
1559 elements, ifsvals.num_elements);
1560
1561 if (ifsDesign->pixmap)
1562 {
1563 g_object_unref (ifsDesign->pixmap);
1564 }
1565 ifsDesign->pixmap = gdk_pixmap_new (gtk_widget_get_window (widget),
1566 allocation.width,
1567 allocation.height,
1568 -1); /* Is this correct? */
1569
1570 return FALSE;
1571 }
1572
1573 static gint
design_area_button_press(GtkWidget * widget,GdkEventButton * event)1574 design_area_button_press (GtkWidget *widget,
1575 GdkEventButton *event)
1576 {
1577 GtkAllocation allocation;
1578 gint i;
1579 gint old_current;
1580
1581 gtk_widget_get_allocation (ifsDesign->area, &allocation);
1582
1583 gtk_widget_grab_focus (widget);
1584
1585 if (gdk_event_triggers_context_menu ((GdkEvent *) event))
1586 {
1587 GtkWidget *menu =
1588 gtk_ui_manager_get_widget (ifsDesign->ui_manager,
1589 "/dummy-menubar/ifs-compose-menu");
1590
1591 if (GTK_IS_MENU_ITEM (menu))
1592 menu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (menu));
1593
1594 gtk_menu_set_screen (GTK_MENU (menu), gtk_widget_get_screen (widget));
1595
1596 gtk_menu_popup (GTK_MENU (menu),
1597 NULL, NULL, NULL, NULL,
1598 event->button, event->time);
1599
1600 return FALSE;
1601 }
1602
1603 old_current = ifsD->current_element;
1604 ifsD->current_element = -1;
1605
1606 /* Find out where the button press was */
1607 for (i = 0; i < ifsvals.num_elements; i++)
1608 {
1609 if (ipolygon_contains (elements[i]->click_boundary, event->x, event->y))
1610 {
1611 set_current_element (i);
1612 break;
1613 }
1614 }
1615
1616 /* if the user started manipulating an object, set up a new
1617 position on the undo ring */
1618 if (ifsD->current_element >= 0)
1619 undo_begin ();
1620
1621 if (!(event->state & GDK_SHIFT_MASK)
1622 && ( (ifsD->current_element<0)
1623 || !element_selected[ifsD->current_element] ))
1624 {
1625 for (i = 0; i < ifsvals.num_elements; i++)
1626 element_selected[i] = FALSE;
1627 }
1628
1629 if (ifsD->current_element >= 0)
1630 {
1631 ifsDesign->button_state |= GDK_BUTTON1_MASK;
1632
1633 element_selected[ifsD->current_element] = TRUE;
1634
1635 ifsDesign->num_selected = 0;
1636 ifsDesign->op_xcenter = 0.0;
1637 ifsDesign->op_ycenter = 0.0;
1638 for (i = 0; i < ifsvals.num_elements; i++)
1639 {
1640 if (element_selected[i])
1641 {
1642 ifsD->selected_orig[i] = *elements[i];
1643 ifsDesign->op_xcenter += elements[i]->v.x;
1644 ifsDesign->op_ycenter += elements[i]->v.y;
1645 ifsDesign->num_selected++;
1646 undo_update (i);
1647 }
1648 }
1649 ifsDesign->op_xcenter /= ifsDesign->num_selected;
1650 ifsDesign->op_ycenter /= ifsDesign->num_selected;
1651 ifsDesign->op_x = (gdouble)event->x / allocation.width;
1652 ifsDesign->op_y = (gdouble)event->y / allocation.width;
1653 ifsDesign->op_center_x = ifsvals.center_x;
1654 ifsDesign->op_center_y = ifsvals.center_y;
1655 }
1656 else
1657 {
1658 ifsD->current_element = old_current;
1659 element_selected[old_current] = TRUE;
1660 }
1661
1662 design_area_redraw ();
1663
1664 return FALSE;
1665 }
1666
1667 static gint
design_area_button_release(GtkWidget * widget,GdkEventButton * event)1668 design_area_button_release (GtkWidget *widget,
1669 GdkEventButton *event)
1670 {
1671 if (event->button == 1 &&
1672 (ifsDesign->button_state & GDK_BUTTON1_MASK))
1673 {
1674 ifsDesign->button_state &= ~GDK_BUTTON1_MASK;
1675 ifs_compose_preview ();
1676 }
1677 return FALSE;
1678 }
1679
1680 static gint
design_area_motion(GtkWidget * widget,GdkEventMotion * event)1681 design_area_motion (GtkWidget *widget,
1682 GdkEventMotion *event)
1683 {
1684 GtkAllocation allocation;
1685 gint i;
1686 gdouble xo;
1687 gdouble yo;
1688 gdouble xn;
1689 gdouble yn;
1690 Aff2 trans, t1, t2, t3;
1691
1692 if (! (ifsDesign->button_state & GDK_BUTTON1_MASK))
1693 return FALSE;
1694
1695 gtk_widget_get_allocation (ifsDesign->area, &allocation);
1696
1697 xo = (ifsDesign->op_x - ifsDesign->op_xcenter);
1698 yo = (ifsDesign->op_y - ifsDesign->op_ycenter);
1699 xn = (gdouble) event->x / allocation.width - ifsDesign->op_xcenter;
1700 yn = (gdouble) event->y / allocation.width - ifsDesign->op_ycenter;
1701
1702 switch (ifsDesign->op)
1703 {
1704 case OP_ROTATE:
1705 aff2_translate (&t1,-ifsDesign->op_xcenter * allocation.width,
1706 -ifsDesign->op_ycenter * allocation.width);
1707 aff2_scale (&t2,
1708 sqrt((SQR(xn)+SQR(yn))/(SQR(xo)+SQR(yo))),
1709 0);
1710 aff2_compose (&t3, &t2, &t1);
1711 aff2_rotate (&t1, - atan2(yn, xn) + atan2(yo, xo));
1712 aff2_compose (&t2, &t1, &t3);
1713 aff2_translate (&t3, ifsDesign->op_xcenter * allocation.width,
1714 ifsDesign->op_ycenter * allocation.width);
1715 aff2_compose (&trans, &t3, &t2);
1716 break;
1717
1718 case OP_STRETCH:
1719 aff2_translate (&t1,-ifsDesign->op_xcenter * allocation.width,
1720 -ifsDesign->op_ycenter * allocation.width);
1721 aff2_compute_stretch (&t2, xo, yo, xn, yn);
1722 aff2_compose (&t3, &t2, &t1);
1723 aff2_translate (&t1, ifsDesign->op_xcenter * allocation.width,
1724 ifsDesign->op_ycenter * allocation.width);
1725 aff2_compose (&trans, &t1, &t3);
1726 break;
1727
1728 case OP_TRANSLATE:
1729 aff2_translate (&trans,
1730 (xn-xo) * allocation.width,
1731 (yn-yo) * allocation.width);
1732 break;
1733 }
1734
1735 for (i = 0; i < ifsvals.num_elements; i++)
1736 if (element_selected[i])
1737 {
1738 if (ifsDesign->num_selected == ifsvals.num_elements)
1739 {
1740 gdouble cx, cy;
1741 aff2_invert (&t1, &trans);
1742 aff2_compose (&t2, &trans, &ifsD->selected_orig[i].trans);
1743 aff2_compose (&elements[i]->trans, &t2, &t1);
1744
1745 cx = ifsDesign->op_center_x * allocation.width;
1746 cy = ifsDesign->op_center_y * allocation.width;
1747 aff2_apply (&trans, cx, cy, &cx, &cy);
1748 ifsvals.center_x = cx / allocation.width;
1749 ifsvals.center_y = cy / allocation.width;
1750 }
1751 else
1752 {
1753 aff2_compose (&elements[i]->trans, &trans,
1754 &ifsD->selected_orig[i].trans);
1755 }
1756
1757 aff_element_decompose_trans (elements[i],&elements[i]->trans,
1758 allocation.width, allocation.height,
1759 ifsvals.center_x, ifsvals.center_y);
1760 aff_element_compute_trans (elements[i],
1761 allocation.width, allocation.height,
1762 ifsvals.center_x, ifsvals.center_y);
1763 }
1764
1765 update_values ();
1766 design_area_redraw ();
1767
1768 /* Ask for more motion events in case the event was a hint */
1769 gdk_event_request_motions (event);
1770
1771 return FALSE;
1772 }
1773
1774 static void
design_area_redraw(void)1775 design_area_redraw (void)
1776 {
1777 GtkAllocation allocation;
1778 gint i;
1779
1780 gtk_widget_get_allocation (ifsDesign->area, &allocation);
1781
1782 for (i = 0; i < ifsvals.num_elements; i++)
1783 aff_element_compute_boundary (elements[i],
1784 allocation.width, allocation.height,
1785 elements, ifsvals.num_elements);
1786
1787 gtk_widget_queue_draw (ifsDesign->area);
1788 }
1789
1790 /* Undo ring functions */
1791 static void
undo_begin(void)1792 undo_begin (void)
1793 {
1794 gint i, j;
1795 gint to_delete;
1796 gint new_index;
1797
1798 if (undo_cur == UNDO_LEVELS-1)
1799 {
1800 to_delete = 1;
1801 undo_start = (undo_start + 1) % UNDO_LEVELS;
1802 }
1803 else
1804 {
1805 undo_cur++;
1806 to_delete = undo_num - undo_cur;
1807 }
1808
1809 undo_num = undo_num - to_delete + 1;
1810 new_index = (undo_start + undo_cur) % UNDO_LEVELS;
1811
1812 /* remove any redo elements or the oldest element if necessary */
1813 for (j = new_index; to_delete > 0; j = (j+1) % UNDO_LEVELS, to_delete--)
1814 {
1815 for (i = 0; i < undo_ring[j].ifsvals.num_elements; i++)
1816 if (undo_ring[j].elements[i])
1817 aff_element_free (undo_ring[j].elements[i]);
1818 g_free (undo_ring[j].elements);
1819 g_free (undo_ring[j].element_selected);
1820 }
1821
1822 undo_ring[new_index].ifsvals = ifsvals;
1823 undo_ring[new_index].elements = g_new (AffElement *,ifsvals.num_elements);
1824 undo_ring[new_index].element_selected = g_new (gboolean,
1825 ifsvals.num_elements);
1826 undo_ring[new_index].current_element = ifsD->current_element;
1827
1828 for (i = 0; i < ifsvals.num_elements; i++)
1829 {
1830 undo_ring[new_index].elements[i] = NULL;
1831 undo_ring[new_index].element_selected[i] = element_selected[i];
1832 }
1833
1834 design_op_actions_update ();
1835 }
1836
1837 static void
undo_update(gint el)1838 undo_update (gint el)
1839 {
1840 AffElement *elem;
1841 /* initialize */
1842
1843 elem = NULL;
1844
1845 if (!undo_ring[(undo_start + undo_cur) % UNDO_LEVELS].elements[el])
1846 undo_ring[(undo_start + undo_cur) % UNDO_LEVELS].elements[el]
1847 = elem = g_new (AffElement, 1);
1848
1849 *elem = *elements[el];
1850 elem->draw_boundary = NULL;
1851 elem->click_boundary = NULL;
1852 }
1853
1854 static void
undo_exchange(gint el)1855 undo_exchange (gint el)
1856 {
1857 GtkAllocation allocation;
1858 gint i;
1859 AffElement **telements;
1860 gboolean *tselected;
1861 IfsComposeVals tifsvals;
1862 gint tcurrent;
1863
1864 gtk_widget_get_allocation (ifsDesign->area, &allocation);
1865
1866 /* swap the arrays and values*/
1867 telements = elements;
1868 elements = undo_ring[el].elements;
1869 undo_ring[el].elements = telements;
1870
1871 tifsvals = ifsvals;
1872 ifsvals = undo_ring[el].ifsvals;
1873 undo_ring[el].ifsvals = tifsvals;
1874
1875 tselected = element_selected;
1876 element_selected = undo_ring[el].element_selected;
1877 undo_ring[el].element_selected = tselected;
1878
1879 tcurrent = ifsD->current_element;
1880 ifsD->current_element = undo_ring[el].current_element;
1881 undo_ring[el].current_element = tcurrent;
1882
1883 /* now swap back any unchanged elements */
1884 for (i = 0; i < ifsvals.num_elements; i++)
1885 if (!elements[i])
1886 {
1887 elements[i] = undo_ring[el].elements[i];
1888 undo_ring[el].elements[i] = NULL;
1889 }
1890 else
1891 aff_element_compute_trans (elements[i],
1892 allocation.width, allocation.height,
1893 ifsvals.center_x, ifsvals.center_y);
1894
1895 set_current_element (ifsD->current_element);
1896
1897 design_area_redraw ();
1898
1899 ifs_compose_preview ();
1900 }
1901
1902 static void
undo(void)1903 undo (void)
1904 {
1905 if (undo_cur >= 0)
1906 {
1907 undo_exchange ((undo_start + undo_cur) % UNDO_LEVELS);
1908 undo_cur--;
1909 }
1910
1911 design_op_actions_update ();
1912 }
1913
1914 static void
redo(void)1915 redo (void)
1916 {
1917 if (undo_cur != undo_num - 1)
1918 {
1919 undo_cur++;
1920 undo_exchange ((undo_start + undo_cur) % UNDO_LEVELS);
1921 }
1922
1923 design_op_actions_update ();
1924 }
1925
1926 static void
design_area_select_all_callback(GtkWidget * widget,gpointer data)1927 design_area_select_all_callback (GtkWidget *widget,
1928 gpointer data)
1929 {
1930 gint i;
1931
1932 for (i = 0; i < ifsvals.num_elements; i++)
1933 element_selected[i] = TRUE;
1934
1935 design_area_redraw ();
1936 }
1937
1938 /* Interface functions */
1939
1940 static void
val_changed_update(void)1941 val_changed_update (void)
1942 {
1943 GtkAllocation allocation;
1944 AffElement *cur;
1945
1946 if (ifsD->in_update)
1947 return;
1948
1949 gtk_widget_get_allocation (ifsDesign->area, &allocation);
1950
1951 cur = elements[ifsD->current_element];
1952
1953 undo_begin ();
1954 undo_update (ifsD->current_element);
1955
1956 cur->v = ifsD->current_vals;
1957 cur->v.theta *= G_PI/180.0;
1958 aff_element_compute_trans (cur,
1959 allocation.width, allocation.height,
1960 ifsvals.center_x, ifsvals.center_y);
1961 aff_element_compute_color_trans (cur);
1962
1963 design_area_redraw ();
1964
1965 ifs_compose_preview ();
1966 }
1967
1968 /* Pseudo-widget representing a color mapping */
1969
1970 #define COLOR_SAMPLE_SIZE 30
1971
1972 static ColorMap *
color_map_create(const gchar * name,GimpRGB * orig_color,GimpRGB * data,gboolean fixed_point)1973 color_map_create (const gchar *name,
1974 GimpRGB *orig_color,
1975 GimpRGB *data,
1976 gboolean fixed_point)
1977 {
1978 GtkWidget *frame;
1979 GtkWidget *arrow;
1980 ColorMap *color_map = g_new (ColorMap, 1);
1981
1982 gimp_rgb_set_alpha (data, 1.0);
1983 color_map->color = data;
1984 color_map->fixed_point = fixed_point;
1985 color_map->hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 2);
1986
1987 frame = gtk_frame_new (NULL);
1988 gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
1989 gtk_box_pack_start (GTK_BOX (color_map->hbox), frame, FALSE, FALSE, 0);
1990 gtk_widget_show (frame);
1991
1992 color_map->orig_preview =
1993 gimp_color_area_new (fixed_point ? data : orig_color,
1994 GIMP_COLOR_AREA_FLAT, 0);
1995 gtk_drag_dest_unset (color_map->orig_preview);
1996 gtk_widget_set_size_request (color_map->orig_preview,
1997 COLOR_SAMPLE_SIZE, COLOR_SAMPLE_SIZE);
1998 gtk_container_add (GTK_CONTAINER (frame), color_map->orig_preview);
1999 gtk_widget_show (color_map->orig_preview);
2000
2001 arrow = gtk_arrow_new (GTK_ARROW_RIGHT, GTK_SHADOW_IN);
2002 gtk_box_pack_start (GTK_BOX (color_map->hbox), arrow, FALSE, FALSE, 0);
2003 gtk_widget_show (arrow);
2004
2005 color_map->button = gimp_color_button_new (name,
2006 COLOR_SAMPLE_SIZE,
2007 COLOR_SAMPLE_SIZE,
2008 data,
2009 GIMP_COLOR_AREA_FLAT);
2010 gtk_box_pack_start (GTK_BOX (color_map->hbox), color_map->button,
2011 FALSE, FALSE, 0);
2012 gtk_widget_show (color_map->button);
2013
2014 g_signal_connect (color_map->button, "color-changed",
2015 G_CALLBACK (gimp_color_button_get_color),
2016 data);
2017
2018 g_signal_connect (color_map->button, "color-changed",
2019 G_CALLBACK (color_map_color_changed_cb),
2020 color_map);
2021
2022 return color_map;
2023 }
2024
2025 static void
color_map_color_changed_cb(GtkWidget * widget,ColorMap * color_map)2026 color_map_color_changed_cb (GtkWidget *widget,
2027 ColorMap *color_map)
2028 {
2029 if (ifsD->in_update)
2030 return;
2031
2032 undo_begin ();
2033 undo_update (ifsD->current_element);
2034
2035 elements[ifsD->current_element]->v = ifsD->current_vals;
2036 elements[ifsD->current_element]->v.theta *= G_PI/180.0;
2037 aff_element_compute_color_trans (elements[ifsD->current_element]);
2038
2039 update_values ();
2040
2041 ifs_compose_preview ();
2042 }
2043
2044 static void
color_map_update(ColorMap * color_map)2045 color_map_update (ColorMap *color_map)
2046 {
2047 gimp_color_button_set_color (GIMP_COLOR_BUTTON (color_map->button),
2048 color_map->color);
2049
2050 if (color_map->fixed_point)
2051 gimp_color_area_set_color (GIMP_COLOR_AREA (color_map->orig_preview),
2052 color_map->color);
2053 }
2054
2055 static void
simple_color_set_sensitive(void)2056 simple_color_set_sensitive (void)
2057 {
2058 gint sc = elements[ifsD->current_element]->v.simple_color;
2059
2060 gtk_widget_set_sensitive (ifsD->target_cmap->hbox, sc);
2061 gtk_widget_set_sensitive (ifsD->hue_scale_pair->scale, sc);
2062 gtk_widget_set_sensitive (ifsD->hue_scale_pair->spin, sc);
2063 gtk_widget_set_sensitive (ifsD->value_scale_pair->scale, sc);
2064 gtk_widget_set_sensitive (ifsD->value_scale_pair->spin, sc);
2065
2066 gtk_widget_set_sensitive (ifsD->red_cmap->hbox, !sc);
2067 gtk_widget_set_sensitive (ifsD->green_cmap->hbox, !sc);
2068 gtk_widget_set_sensitive (ifsD->blue_cmap->hbox, !sc);
2069 gtk_widget_set_sensitive (ifsD->black_cmap->hbox, !sc);
2070 }
2071
2072 static void
simple_color_toggled(GtkWidget * widget,gpointer data)2073 simple_color_toggled (GtkWidget *widget,
2074 gpointer data)
2075 {
2076 AffElement *cur = elements[ifsD->current_element];
2077
2078 cur->v.simple_color = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget));
2079
2080 ifsD->current_vals.simple_color = cur->v.simple_color;
2081
2082 if (cur->v.simple_color)
2083 aff_element_compute_color_trans (cur);
2084
2085 val_changed_update ();
2086 simple_color_set_sensitive ();
2087 }
2088
2089 /* Generic mechanism for scale/entry combination (possibly without
2090 scale) */
2091
2092 static ValuePair *
value_pair_create(gpointer data,gdouble lower,gdouble upper,gboolean create_scale,ValuePairType type)2093 value_pair_create (gpointer data,
2094 gdouble lower,
2095 gdouble upper,
2096 gboolean create_scale,
2097 ValuePairType type)
2098 {
2099
2100 ValuePair *value_pair = g_new (ValuePair, 1);
2101
2102 value_pair->data.d = data;
2103 value_pair->type = type;
2104 value_pair->timeout_id = 0;
2105
2106 value_pair->adjustment = (GtkAdjustment *)
2107 gtk_adjustment_new (1.0, lower, upper,
2108 (upper - lower) / 100,
2109 (upper - lower) / 10,
2110 0.0);
2111 value_pair->spin = gimp_spin_button_new (value_pair->adjustment, 1.0, 3);
2112 gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (value_pair->spin), TRUE);
2113 gtk_widget_set_size_request (value_pair->spin, 72, -1);
2114
2115 g_signal_connect (value_pair->adjustment, "value-changed",
2116 G_CALLBACK (value_pair_scale_callback),
2117 value_pair);
2118
2119 if (create_scale)
2120 {
2121 value_pair->scale = gtk_scale_new (GTK_ORIENTATION_HORIZONTAL,
2122 value_pair->adjustment);
2123
2124 if (type == VALUE_PAIR_INT)
2125 gtk_scale_set_digits (GTK_SCALE (value_pair->scale), 0);
2126 else
2127 gtk_scale_set_digits (GTK_SCALE (value_pair->scale), 3);
2128
2129 gtk_scale_set_draw_value (GTK_SCALE (value_pair->scale), FALSE);
2130 }
2131 else
2132 {
2133 value_pair->scale = NULL;
2134 }
2135
2136 return value_pair;
2137 }
2138
2139 static void
value_pair_update(ValuePair * value_pair)2140 value_pair_update (ValuePair *value_pair)
2141 {
2142 if (value_pair->type == VALUE_PAIR_INT)
2143 gtk_adjustment_set_value (value_pair->adjustment, *value_pair->data.i);
2144 else
2145 gtk_adjustment_set_value (value_pair->adjustment, *value_pair->data.d);
2146
2147 }
2148
2149 static gboolean
value_pair_scale_callback_real(gpointer data)2150 value_pair_scale_callback_real (gpointer data)
2151 {
2152 ValuePair *value_pair = data;
2153 gint changed = FALSE;
2154
2155 if (value_pair->type == VALUE_PAIR_DOUBLE)
2156 {
2157 if ((gdouble) *value_pair->data.d !=
2158 gtk_adjustment_get_value (value_pair->adjustment))
2159 {
2160 changed = TRUE;
2161 *value_pair->data.d = gtk_adjustment_get_value (value_pair->adjustment);
2162 }
2163 }
2164 else
2165 {
2166 if (*value_pair->data.i !=
2167 (gint) gtk_adjustment_get_value (value_pair->adjustment))
2168 {
2169 changed = TRUE;
2170 *value_pair->data.i = gtk_adjustment_get_value (value_pair->adjustment);
2171 }
2172 }
2173
2174 if (changed)
2175 val_changed_update ();
2176
2177 value_pair->timeout_id = 0;
2178
2179 return FALSE;
2180 }
2181
2182 static void
value_pair_scale_callback(GtkAdjustment * adjustment,ValuePair * value_pair)2183 value_pair_scale_callback (GtkAdjustment *adjustment,
2184 ValuePair *value_pair)
2185 {
2186 if (value_pair->timeout_id != 0)
2187 return;
2188
2189 value_pair->timeout_id = g_timeout_add (500, /* update every half second */
2190 value_pair_scale_callback_real,
2191 value_pair);
2192 }
2193
2194 static void
design_op_update_callback(GtkRadioAction * action,GtkRadioAction * current,gpointer data)2195 design_op_update_callback (GtkRadioAction *action,
2196 GtkRadioAction *current,
2197 gpointer data)
2198 {
2199 ifsDesign->op = gtk_radio_action_get_current_value (action);
2200
2201 /* cursor switch */
2202 if (gtk_widget_get_realized (ifsDesign->area))
2203 design_area_realize (ifsDesign->area);
2204 }
2205
2206 static void
recompute_center_cb(GtkWidget * widget,gpointer data)2207 recompute_center_cb (GtkWidget *widget,
2208 gpointer data)
2209 {
2210 recompute_center (TRUE);
2211 }
2212
2213 static void
recompute_center(gboolean save_undo)2214 recompute_center (gboolean save_undo)
2215 {
2216 GtkAllocation allocation;
2217 gint i;
2218 gdouble x, y;
2219 gdouble center_x = 0.0;
2220 gdouble center_y = 0.0;
2221
2222 gtk_widget_get_allocation (ifsDesign->area, &allocation);
2223
2224 if (save_undo)
2225 undo_begin ();
2226
2227 for (i = 0; i < ifsvals.num_elements; i++)
2228 {
2229 if (save_undo)
2230 undo_update (i);
2231
2232 aff_element_compute_trans (elements[i],1, ifsvals.aspect_ratio,
2233 ifsvals.center_x, ifsvals.center_y);
2234 aff2_fixed_point (&elements[i]->trans, &x, &y);
2235 center_x += x;
2236 center_y += y;
2237 }
2238
2239 ifsvals.center_x = center_x/ifsvals.num_elements;
2240 ifsvals.center_y = center_y/ifsvals.num_elements;
2241
2242 for (i = 0; i < ifsvals.num_elements; i++)
2243 {
2244 aff_element_decompose_trans (elements[i],&elements[i]->trans,
2245 1, ifsvals.aspect_ratio,
2246 ifsvals.center_x, ifsvals.center_y);
2247 }
2248
2249 if (allocation.width > 1 && allocation.height > 1)
2250 {
2251 for (i = 0; i < ifsvals.num_elements; i++)
2252 aff_element_compute_trans (elements[i],
2253 allocation.width, allocation.height,
2254 ifsvals.center_x, ifsvals.center_y);
2255 design_area_redraw ();
2256 update_values ();
2257 }
2258 }
2259
2260 static void
flip_check_button_callback(GtkWidget * widget,gpointer data)2261 flip_check_button_callback (GtkWidget *widget,
2262 gpointer data)
2263 {
2264 GtkAllocation allocation;
2265 guint i;
2266 gboolean active;
2267
2268 if (ifsD->in_update)
2269 return;
2270
2271 gtk_widget_get_allocation (ifsDesign->area, &allocation);
2272
2273 active = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget));
2274
2275 undo_begin ();
2276 for (i = 0; i < ifsvals.num_elements; i++)
2277 {
2278 if (element_selected[i])
2279 {
2280 undo_update (i);
2281 elements[i]->v.flip = active;
2282 aff_element_compute_trans (elements[i],
2283 allocation.width, allocation.height,
2284 ifsvals.center_x, ifsvals.center_y);
2285 }
2286 }
2287
2288 update_values ();
2289 design_area_redraw ();
2290
2291 ifs_compose_preview ();
2292 }
2293
2294 static void
ifs_compose_set_defaults(void)2295 ifs_compose_set_defaults (void)
2296 {
2297 gint i;
2298 GimpRGB color;
2299
2300 gimp_context_get_foreground (&color);
2301
2302 ifsvals.aspect_ratio =
2303 (gdouble)ifsD->drawable_height / ifsD->drawable_width;
2304
2305 for (i = 0; i < ifsvals.num_elements; i++)
2306 aff_element_free (elements[i]);
2307 count_for_naming = 0;
2308
2309 ifsvals.num_elements = 3;
2310 elements = g_realloc (elements, ifsvals.num_elements * sizeof(AffElement *));
2311 element_selected = g_realloc (element_selected,
2312 ifsvals.num_elements * sizeof(gboolean));
2313
2314 elements[0] = aff_element_new (0.3, 0.37 * ifsvals.aspect_ratio, &color,
2315 ++count_for_naming);
2316 element_selected[0] = FALSE;
2317 elements[1] = aff_element_new (0.7, 0.37 * ifsvals.aspect_ratio, &color,
2318 ++count_for_naming);
2319 element_selected[1] = FALSE;
2320 elements[2] = aff_element_new (0.5, 0.7 * ifsvals.aspect_ratio, &color,
2321 ++count_for_naming);
2322 element_selected[2] = FALSE;
2323
2324 ifsvals.center_x = 0.5;
2325 ifsvals.center_y = 0.5 * ifsvals.aspect_ratio;
2326 ifsvals.iterations = ifsD->drawable_height * ifsD->drawable_width;
2327 ifsvals.subdivide = 3;
2328 ifsvals.max_memory = 4096;
2329
2330 if (ifsOptD)
2331 {
2332 value_pair_update (ifsOptD->iterations_pair);
2333 value_pair_update (ifsOptD->subdivide_pair);
2334 value_pair_update (ifsOptD->radius_pair);
2335 value_pair_update (ifsOptD->memory_pair);
2336 }
2337
2338 ifsvals.radius = 0.7;
2339
2340 set_current_element (0);
2341 element_selected[0] = TRUE;
2342 recompute_center (FALSE);
2343
2344 if (ifsD->selected_orig)
2345 g_free (ifsD->selected_orig);
2346
2347 ifsD->selected_orig = g_new (AffElement, ifsvals.num_elements);
2348 }
2349
2350 /* show a transient message dialog */
2351 static void
ifscompose_message_dialog(GtkMessageType type,GtkWindow * parent,const gchar * title,const gchar * message)2352 ifscompose_message_dialog (GtkMessageType type,
2353 GtkWindow *parent,
2354 const gchar *title,
2355 const gchar *message)
2356 {
2357 GtkWidget *dialog;
2358
2359 dialog = gtk_message_dialog_new (parent, 0, type, GTK_BUTTONS_OK,
2360 "%s", message);
2361
2362 if (title)
2363 gtk_window_set_title (GTK_WINDOW (dialog), title);
2364
2365 gtk_window_set_role (GTK_WINDOW (dialog), "ifscompose-message");
2366 gtk_dialog_run (GTK_DIALOG (dialog));
2367 gtk_widget_destroy (dialog);
2368 }
2369
2370 /* save an ifs file */
2371 static void
ifsfile_save_response(GtkWidget * dialog,gint response_id,gpointer data)2372 ifsfile_save_response (GtkWidget *dialog,
2373 gint response_id,
2374 gpointer data)
2375 {
2376 if (response_id == GTK_RESPONSE_OK)
2377 {
2378 gchar *filename;
2379 gchar *str;
2380 FILE *fh;
2381
2382 filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
2383
2384 str = ifsvals_stringify (&ifsvals, elements);
2385
2386 fh = g_fopen (filename, "wb");
2387 if (! fh)
2388 {
2389 gchar *message =
2390 g_strdup_printf (_("Could not open '%s' for writing: %s"),
2391 gimp_filename_to_utf8 (filename),
2392 g_strerror (errno));
2393
2394 ifscompose_message_dialog (GTK_MESSAGE_ERROR, GTK_WINDOW (dialog),
2395 _("Save failed"), message);
2396
2397 g_free (message);
2398 g_free (filename);
2399
2400 return;
2401 }
2402
2403 fputs (str, fh);
2404 fclose (fh);
2405 }
2406
2407 gtk_widget_destroy (dialog);
2408 }
2409
2410 /* replace ifsvals and elements with specified new values
2411 * recompute and update everything necessary */
2412 static void
ifsfile_replace_ifsvals(IfsComposeVals * new_ifsvals,AffElement ** new_elements)2413 ifsfile_replace_ifsvals (IfsComposeVals *new_ifsvals,
2414 AffElement **new_elements)
2415 {
2416 GtkAllocation allocation;
2417 guint i;
2418
2419 gtk_widget_get_allocation (ifsDesign->area, &allocation);
2420
2421 for (i = 0; i < ifsvals.num_elements; i++)
2422 aff_element_free (elements[i]);
2423 g_free (elements);
2424
2425 ifsvals = *new_ifsvals;
2426 elements = new_elements;
2427 for (i = 0; i < ifsvals.num_elements; i++)
2428 {
2429 aff_element_compute_trans (elements[i],
2430 allocation.width, allocation.height,
2431 ifsvals.center_x, ifsvals.center_y);
2432 aff_element_compute_color_trans (elements[i]);
2433 }
2434
2435 element_selected = g_realloc (element_selected,
2436 ifsvals.num_elements * sizeof(gboolean));
2437 for (i = 0; i < ifsvals.num_elements; i++)
2438 element_selected[i] = FALSE;
2439
2440 if (ifsOptD)
2441 {
2442 value_pair_update (ifsOptD->iterations_pair);
2443 value_pair_update (ifsOptD->subdivide_pair);
2444 value_pair_update (ifsOptD->radius_pair);
2445 value_pair_update (ifsOptD->memory_pair);
2446 }
2447
2448 set_current_element (0);
2449 element_selected[0] = TRUE;
2450 recompute_center (FALSE);
2451
2452 if (ifsD->selected_orig)
2453 g_free (ifsD->selected_orig);
2454
2455 ifsD->selected_orig = g_new (AffElement, ifsvals.num_elements);
2456 }
2457
2458 /* load an ifs file */
2459 static void
ifsfile_load_response(GtkWidget * dialog,gint response_id,gpointer data)2460 ifsfile_load_response (GtkWidget *dialog,
2461 gint response_id,
2462 gpointer data)
2463 {
2464 if (response_id == GTK_RESPONSE_OK)
2465 {
2466 gchar *filename;
2467 gchar *buffer;
2468 AffElement **new_elements;
2469 IfsComposeVals new_ifsvals;
2470 GError *error = NULL;
2471 guint i;
2472
2473 filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
2474
2475 if (! g_file_get_contents (filename, &buffer, NULL, &error))
2476 {
2477 ifscompose_message_dialog (GTK_MESSAGE_ERROR, GTK_WINDOW (dialog),
2478 _("Open failed"), error->message);
2479 g_error_free (error);
2480 g_free (filename);
2481 return;
2482 }
2483
2484 if (! ifsvals_parse_string (buffer, &new_ifsvals, &new_elements))
2485 {
2486 gchar *message = g_strdup_printf (_("File '%s' doesn't seem to be "
2487 "an IFS Fractal file."),
2488 gimp_filename_to_utf8 (filename));
2489
2490 ifscompose_message_dialog (GTK_MESSAGE_ERROR, GTK_WINDOW (dialog),
2491 _("Open failed"), message);
2492 g_free (filename);
2493 g_free (message);
2494 g_free (buffer);
2495
2496 return;
2497 }
2498
2499 g_free (buffer);
2500 g_free (filename);
2501
2502 undo_begin ();
2503 for (i = 0; i < ifsvals.num_elements; i++)
2504 undo_update (i);
2505
2506 ifsfile_replace_ifsvals (&new_ifsvals, new_elements);
2507
2508 design_op_actions_update ();
2509
2510 ifs_compose_preview ();
2511
2512 design_area_redraw ();
2513 }
2514
2515 gtk_widget_destroy (GTK_WIDGET (dialog));
2516 }
2517
2518 static void
ifs_compose_save(GtkWidget * parent)2519 ifs_compose_save (GtkWidget *parent)
2520 {
2521 static GtkWidget *dialog = NULL;
2522
2523 if (! dialog)
2524 {
2525 dialog =
2526 gtk_file_chooser_dialog_new (_("Save as IFS Fractal file"),
2527 GTK_WINDOW (parent),
2528 GTK_FILE_CHOOSER_ACTION_SAVE,
2529
2530 _("_Cancel"), GTK_RESPONSE_CANCEL,
2531 _("_Save"), GTK_RESPONSE_OK,
2532
2533 NULL);
2534
2535 gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
2536 GTK_RESPONSE_OK,
2537 GTK_RESPONSE_CANCEL,
2538 -1);
2539 gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK);
2540
2541 gtk_file_chooser_set_do_overwrite_confirmation (GTK_FILE_CHOOSER (dialog),
2542 TRUE);
2543
2544 g_signal_connect (dialog, "destroy",
2545 G_CALLBACK (gtk_widget_destroyed),
2546 &dialog);
2547 g_signal_connect (dialog, "response",
2548 G_CALLBACK (ifsfile_save_response),
2549 NULL);
2550 }
2551
2552 gtk_window_present (GTK_WINDOW (dialog));
2553 }
2554
2555 static void
ifs_compose_load(GtkWidget * parent)2556 ifs_compose_load (GtkWidget *parent)
2557 {
2558 static GtkWidget *dialog = NULL;
2559
2560 if (! dialog)
2561 {
2562 dialog =
2563 gtk_file_chooser_dialog_new (_("Open IFS Fractal file"),
2564 GTK_WINDOW (parent),
2565 GTK_FILE_CHOOSER_ACTION_OPEN,
2566
2567 _("_Cancel"), GTK_RESPONSE_CANCEL,
2568 _("_Open"), GTK_RESPONSE_OK,
2569
2570 NULL);
2571
2572 gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
2573 GTK_RESPONSE_OK,
2574 GTK_RESPONSE_CANCEL,
2575 -1);
2576
2577 gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK);
2578
2579 g_signal_connect (dialog, "destroy",
2580 G_CALLBACK (gtk_widget_destroyed),
2581 &dialog);
2582 g_signal_connect (dialog, "response",
2583 G_CALLBACK (ifsfile_load_response),
2584 NULL);
2585 }
2586
2587 gtk_window_present (GTK_WINDOW (dialog));
2588 }
2589
2590 static void
ifs_compose_new_callback(GtkAction * action,gpointer data)2591 ifs_compose_new_callback (GtkAction *action,
2592 gpointer data)
2593 {
2594 GtkAllocation allocation;
2595 GimpRGB color;
2596 gint i;
2597 AffElement *elem;
2598
2599 gtk_widget_get_allocation (ifsDesign->area, &allocation);
2600
2601 undo_begin ();
2602
2603 gimp_context_get_foreground (&color);
2604
2605 elem = aff_element_new (0.5, 0.5 * allocation.height / allocation.width,
2606 &color,
2607 ++count_for_naming);
2608
2609 ifsvals.num_elements++;
2610 elements = g_realloc (elements, ifsvals.num_elements * sizeof (AffElement *));
2611 element_selected = g_realloc (element_selected,
2612 ifsvals.num_elements * sizeof (gboolean));
2613
2614 for (i = 0; i < ifsvals.num_elements-1; i++)
2615 element_selected[i] = FALSE;
2616 element_selected[ifsvals.num_elements-1] = TRUE;
2617
2618 elements[ifsvals.num_elements-1] = elem;
2619 set_current_element (ifsvals.num_elements-1);
2620
2621 ifsD->selected_orig = g_realloc (ifsD->selected_orig,
2622 ifsvals.num_elements * sizeof(AffElement));
2623 aff_element_compute_trans (elem,
2624 allocation.width, allocation.height,
2625 ifsvals.center_x, ifsvals.center_y);
2626
2627 design_area_redraw ();
2628
2629 ifs_compose_preview ();
2630
2631 design_op_actions_update ();
2632 }
2633
2634 static void
ifs_compose_delete_callback(GtkAction * action,gpointer data)2635 ifs_compose_delete_callback (GtkAction *action,
2636 gpointer data)
2637 {
2638 gint i;
2639 gint new_current;
2640
2641 undo_begin ();
2642 undo_update (ifsD->current_element);
2643
2644 aff_element_free (elements[ifsD->current_element]);
2645
2646 if (ifsD->current_element < ifsvals.num_elements-1)
2647 {
2648 undo_update (ifsvals.num_elements-1);
2649 elements[ifsD->current_element] = elements[ifsvals.num_elements-1];
2650 new_current = ifsD->current_element;
2651 }
2652 else
2653 new_current = ifsvals.num_elements-2;
2654
2655 ifsvals.num_elements--;
2656
2657 for (i = 0; i < ifsvals.num_elements; i++)
2658 if (element_selected[i])
2659 {
2660 new_current = i;
2661 break;
2662 }
2663
2664 element_selected[new_current] = TRUE;
2665 set_current_element (new_current);
2666
2667 design_area_redraw ();
2668
2669 ifs_compose_preview ();
2670
2671 design_op_actions_update ();
2672 }
2673
2674 static void
ifs_compose_options_callback(GtkAction * action,gpointer data)2675 ifs_compose_options_callback (GtkAction *action,
2676 gpointer data)
2677 {
2678 ifs_options_dialog (GTK_WIDGET (data));
2679 }
2680
2681 static gint
preview_idle_render(gpointer data)2682 preview_idle_render (gpointer data)
2683 {
2684 GtkAllocation allocation;
2685 gint iterations = PREVIEW_RENDER_CHUNK;
2686 gint i;
2687
2688 gtk_widget_get_allocation (ifsDesign->area, &allocation);
2689
2690 if (iterations > ifsD->preview_iterations)
2691 iterations = ifsD->preview_iterations;
2692
2693 for (i = 0; i < ifsvals.num_elements; i++)
2694 aff_element_compute_trans (elements[i],
2695 allocation.width, allocation.height,
2696 ifsvals.center_x, ifsvals.center_y);
2697
2698 ifs_render (elements, ifsvals.num_elements,
2699 allocation.width, allocation.height,
2700 iterations,&ifsvals, 0, allocation.height,
2701 ifsD->preview_data, NULL, NULL, TRUE);
2702
2703 for (i = 0; i < ifsvals.num_elements; i++)
2704 aff_element_compute_trans (elements[i],
2705 allocation.width, allocation.height,
2706 ifsvals.center_x, ifsvals.center_y);
2707
2708 ifsD->preview_iterations -= iterations;
2709
2710 gimp_preview_area_draw (GIMP_PREVIEW_AREA (ifsD->preview),
2711 0, 0, allocation.width, allocation.height,
2712 GIMP_RGB_IMAGE,
2713 ifsD->preview_data,
2714 allocation.width * 3);
2715
2716 return (ifsD->preview_iterations != 0);
2717 }
2718
2719 static void
ifs_compose_preview(void)2720 ifs_compose_preview (void)
2721 {
2722 /* Expansion isn't really supported for previews */
2723 gint i;
2724 gint width = ifsD->preview_width;
2725 gint height = ifsD->preview_height;
2726 guchar rc, gc, bc;
2727 guchar *ptr;
2728 GimpRGB color;
2729
2730 if (!ifsD->preview_data)
2731 ifsD->preview_data = g_new (guchar, 3 * width * height);
2732
2733 gimp_context_get_background (&color);
2734 gimp_rgb_get_uchar (&color, &rc, &gc, &bc);
2735
2736 ptr = ifsD->preview_data;
2737 for (i = 0; i < width * height; i++)
2738 {
2739 *ptr++ = rc;
2740 *ptr++ = gc;
2741 *ptr++ = bc;
2742 }
2743
2744 if (ifsD->preview_iterations == 0)
2745 g_idle_add (preview_idle_render, NULL);
2746
2747 ifsD->preview_iterations =
2748 ifsvals.iterations * ((gdouble) width * height /
2749 (ifsD->drawable_width * ifsD->drawable_height));
2750 }
2751
2752 static void
ifs_compose_response(GtkWidget * widget,gint response_id,gpointer data)2753 ifs_compose_response (GtkWidget *widget,
2754 gint response_id,
2755 gpointer data)
2756 {
2757 switch (response_id)
2758 {
2759 case RESPONSE_OPEN:
2760 ifs_compose_load (widget);
2761 break;
2762
2763 case RESPONSE_SAVE:
2764 ifs_compose_save (widget);
2765 break;
2766
2767 case RESPONSE_RESET:
2768 {
2769 GtkAllocation allocation;
2770 gint i;
2771
2772 gtk_widget_get_allocation (ifsDesign->area, &allocation);
2773
2774 undo_begin ();
2775 for (i = 0; i < ifsvals.num_elements; i++)
2776 undo_update (i);
2777
2778 ifs_compose_set_defaults ();
2779
2780 ifs_compose_preview ();
2781
2782 for (i = 0; i < ifsvals.num_elements; i++)
2783 aff_element_compute_trans (elements[i],
2784 allocation.width, allocation.height,
2785 ifsvals.center_x, ifsvals.center_y);
2786
2787 design_area_redraw ();
2788 design_op_actions_update ();
2789 }
2790 break;
2791
2792 case GTK_RESPONSE_OK:
2793 ifscint.run = TRUE;
2794
2795 default:
2796 gtk_widget_destroy (widget);
2797 break;
2798 }
2799 }
2800