25
* 1. Run in non-interactive mode (need to figure out useful
26
* way for a script to give the 19N paramters for an image).
27
* Perhaps just support saving parameters to a file, script
28
* passes file name. (The save-to-file part is already done [Yeti])
29
* 2. Save settings on a per-layer basis (long term, needs GIMP
30
* support to do properly). Load/save from affine parameters?
31
* 3. Figure out if we need multiple phases for supersampled
33
* 4. (minor) Make undo work correctly when focus is in entry widget.
34
* (This seems fixed now (by mere change to spinbuttons) [Yeti])
25
* 1. Run in non-interactive mode (need to figure out useful way for a
26
* script to give the 19N paramters for an image). Perhaps just
27
* support saving parameters to a file, script passes file name.
28
* 2. Figure out if we need multiple phases for supersampled brushes.
37
31
#include "config.h"
41
33
#include <string.h>
36
#include <glib/gstdio.h>
46
38
#include <libgimp/gimp.h>
47
39
#include <libgimp/gimpui.h>
334
static GimpParamDef args[] =
322
static const GimpParamDef args[] =
336
{ GIMP_PDB_INT32, "run_mode", "Interactive, non-interactive" },
324
{ GIMP_PDB_INT32, "run-mode", "Interactive, non-interactive" },
337
325
{ GIMP_PDB_IMAGE, "image", "Input image" },
338
326
{ GIMP_PDB_DRAWABLE, "drawable", "Input drawable" },
341
static GimpParamDef *return_vals = NULL;
329
static const GimpParamDef *return_vals = NULL;
342
330
static int nreturn_vals = 0;
344
gimp_install_procedure ("plug_in_ifs_compose",
345
"Create an Iterated Function System (IFS) Fractal",
332
gimp_install_procedure (IFSCOMPOSE_PROC,
333
N_("Create an Iterated Function System (IFS) fractal"),
346
334
"Interactively create an Iterated Function System "
347
335
"fractal. Use the window on the upper left to adjust "
348
336
"the component transformations of the fractal. The "
427
413
count_for_naming = ifsvals.num_elements;
429
415
/* First acquire information with a dialog */
430
if (! ifs_compose_dialog (active_drawable))
416
if (! ifs_compose_dialog (drawable))
434
420
case GIMP_RUN_NONINTERACTIVE:
435
/* Make sure all the arguments are there! */
436
421
status = GIMP_PDB_CALLING_ERROR;
439
424
case GIMP_RUN_WITH_LAST_VALS:
440
/* Possibly retrieve data */
444
length = gimp_get_data_size (IFSCOMPOSE_DATA);
447
gchar *data = g_new (gchar, length);
449
gimp_get_data (IFSCOMPOSE_DATA, data);
450
ifsvals_parse_string (data, &ifsvals, &elements);
455
ifs_compose_set_defaults ();
426
gint length = gimp_get_data_size (IFSCOMPOSE_PROC);
430
gchar *data = g_new (gchar, length);
432
gimp_get_data (IFSCOMPOSE_PROC, data);
433
ifsvals_parse_string (data, &ifsvals, &elements);
438
ifs_compose_set_defaults ();
464
447
/* Render the fractal */
465
448
if ((status == GIMP_PDB_SUCCESS) &&
466
(gimp_drawable_is_rgb (active_drawable->drawable_id) ||
467
gimp_drawable_is_gray (active_drawable->drawable_id)))
449
(gimp_drawable_is_rgb (drawable->drawable_id) ||
450
gimp_drawable_is_gray (drawable->drawable_id)))
469
452
/* set the tile cache size so that the operation works well */
470
gimp_tile_cache_ntiles (2 * (MAX (active_drawable->width,
471
active_drawable->height) /
453
gimp_tile_cache_ntiles (2 * (MAX (drawable->width, drawable->height) /
472
454
gimp_tile_width () + 1));
474
456
if (run_mode == GIMP_RUN_INTERACTIVE)
479
461
gimp_image_undo_group_start (image_id);
481
463
/* run the effect */
482
ifs_compose (active_drawable);
464
ifs_compose (drawable);
484
466
/* Store data for next invocation - both globally and
485
467
* as a parasite on this layer
487
469
str = ifsvals_stringify (&ifsvals, elements);
489
gimp_set_data (IFSCOMPOSE_DATA, str, strlen (str) + 1);
471
gimp_set_data (IFSCOMPOSE_PROC, str, strlen (str) + 1);
491
473
parasite = gimp_parasite_new (IFSCOMPOSE_PARASITE,
492
474
GIMP_PARASITE_PERSISTENT |
493
475
GIMP_PARASITE_UNDOABLE,
494
476
strlen (str) + 1, str);
495
gimp_drawable_parasite_attach (active_drawable->drawable_id,
477
gimp_drawable_parasite_attach (drawable->drawable_id, parasite);
497
478
gimp_parasite_free (parasite);
671
652
GTK_FILL, 0, 0, 0);
672
653
gtk_widget_show (ifsD->target_cmap->hbox);
674
label = gtk_label_new (_("Scale Hue by:"));
655
label = gtk_label_new (_("Scale hue by:"));
675
656
gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
676
657
gtk_table_attach (GTK_TABLE (table), label, 2, 3, 0, 1,
677
658
GTK_FILL, GTK_FILL, 0, 0);
686
667
GTK_FILL, GTK_FILL, 0, 0);
687
668
gtk_widget_show (ifsD->hue_scale_pair->spin);
689
label = gtk_label_new (_("Scale Value by:"));
670
label = gtk_label_new (_("Scale value by:"));
690
671
gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
691
672
gtk_table_attach (GTK_TABLE (table), label, 2, 3, 1, 2,
692
673
GTK_FILL, GTK_FILL, 0, 0);
709
690
group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (ifsD->full_button));
710
691
gtk_widget_show (ifsD->full_button);
712
gimp_rgb_set (&color, 1.0, 0.0, 0.0);
693
gimp_rgb_parse_name (&color, "red", -1);
694
gimp_rgb_set_alpha (&color, 1.0);
713
695
ifsD->red_cmap = color_map_create (_("IFS Fractal: Red"), &color,
714
696
&ifsD->current_vals.red_color, FALSE);
715
697
gtk_table_attach (GTK_TABLE (table), ifsD->red_cmap->hbox, 1, 2, 2, 3,
716
698
GTK_FILL, GTK_FILL, 0, 0);
717
699
gtk_widget_show (ifsD->red_cmap->hbox);
719
gimp_rgb_set (&color, 0.0, 1.0, 0.0);
701
gimp_rgb_parse_name (&color, "green", -1);
702
gimp_rgb_set_alpha (&color, 1.0);
720
703
ifsD->green_cmap = color_map_create (_("IFS Fractal: Green"), &color,
721
704
&ifsD->current_vals.green_color, FALSE);
722
705
gtk_table_attach (GTK_TABLE (table), ifsD->green_cmap->hbox, 2, 3, 2, 3,
723
706
GTK_FILL, GTK_FILL, 0, 0);
724
707
gtk_widget_show (ifsD->green_cmap->hbox);
726
gimp_rgb_set (&color, 0.0, 0.0, 1.0);
709
gimp_rgb_parse_name (&color, "blue", -1);
710
gimp_rgb_set_alpha (&color, 1.0);
727
711
ifsD->blue_cmap = color_map_create (_("IFS Fractal: Blue"), &color,
728
712
&ifsD->current_vals.blue_color, FALSE);
729
713
gtk_table_attach (GTK_TABLE (table), ifsD->blue_cmap->hbox, 3, 4, 2, 3,
730
714
GTK_FILL, GTK_FILL, 0, 0);
731
715
gtk_widget_show (ifsD->blue_cmap->hbox);
733
gimp_rgb_set (&color, 0.0, 0.0, 0.0);
717
gimp_rgb_parse_name (&color, "black", -1);
718
gimp_rgb_set_alpha (&color, 1.0);
734
719
ifsD->black_cmap = color_map_create (_("IFS Fractal: Black"), &color,
735
720
&ifsD->current_vals.black_color, FALSE);
736
721
gtk_table_attach (GTK_TABLE (table), ifsD->black_cmap->hbox, 4, 5, 2, 3,
784
769
dialog = gimp_dialog_new (_("IFS Fractal"), "ifscompose",
786
gimp_standard_help_func, HELP_ID,
771
gimp_standard_help_func, IFSCOMPOSE_PROC,
788
773
GTK_STOCK_OPEN, RESPONSE_OPEN,
789
774
GTK_STOCK_SAVE, RESPONSE_SAVE,
1010
1005
g_signal_connect (ifsDesign->area, "realize",
1011
1006
G_CALLBACK (design_area_realize),
1013
g_signal_connect (ifsDesign->area, "expose_event",
1008
g_signal_connect (ifsDesign->area, "expose-event",
1014
1009
G_CALLBACK (design_area_expose),
1016
g_signal_connect (ifsDesign->area, "button_press_event",
1011
g_signal_connect (ifsDesign->area, "button-press-event",
1017
1012
G_CALLBACK (design_area_button_press),
1019
g_signal_connect (ifsDesign->area, "button_release_event",
1014
g_signal_connect (ifsDesign->area, "button-release-event",
1020
1015
G_CALLBACK (design_area_button_release),
1022
g_signal_connect (ifsDesign->area, "motion_notify_event",
1017
g_signal_connect (ifsDesign->area, "motion-notify-event",
1023
1018
G_CALLBACK (design_area_motion),
1025
g_signal_connect (ifsDesign->area, "configure_event",
1020
g_signal_connect (ifsDesign->area, "configure-event",
1026
1021
G_CALLBACK (design_area_configure),
1028
1023
gtk_widget_set_events (ifsDesign->area,
1068
1063
G_CALLBACK (recompute_center_cb) },
1070
1065
{ "options", GTK_STOCK_PREFERENCES,
1071
N_("Render options"), NULL, NULL,
1066
N_("Render Options"), NULL, NULL,
1072
1067
G_CALLBACK (ifs_compose_options_callback) }
1074
1069
static GtkRadioActionEntry radio_actions[] =
1153
1148
design_op_actions_update (void)
1155
g_object_set (gtk_ui_manager_get_action (ifsDesign->ui_manager,
1156
"/ui/dummy-menubar/ifs-compose-menu/undo"),
1157
"sensitive", undo_cur >= 0,
1159
g_object_set (gtk_ui_manager_get_action (ifsDesign->ui_manager,
1160
"/ui/dummy-menubar/ifs-compose-menu/redo"),
1161
"sensitive", undo_cur != undo_num - 1,
1163
g_object_set (gtk_ui_manager_get_action (ifsDesign->ui_manager,
1164
"/ui/dummy-menubar/ifs-compose-menu/delete"),
1165
"sensitive", ifsvals.num_elements > 2,
1152
act = gtk_ui_manager_get_action (ifsDesign->ui_manager,
1153
"/ui/dummy-menubar/ifs-compose-menu/undo");
1154
gtk_action_set_sensitive (act, undo_cur >= 0);
1156
act = gtk_ui_manager_get_action (ifsDesign->ui_manager,
1157
"/ui/dummy-menubar/ifs-compose-menu/redo");
1158
gtk_action_set_sensitive (act, undo_cur != undo_num - 1);
1160
act = gtk_ui_manager_get_action (ifsDesign->ui_manager,
1161
"/ui/dummy-menubar/ifs-compose-menu/delete");
1162
gtk_action_set_sensitive (act, ifsvals.num_elements > 2);
1198
1194
FALSE, FALSE, 0);
1199
1195
gtk_widget_show (table);
1201
label = gtk_label_new (_("Max. Memory:"));
1197
label = gtk_label_new (_("Max. memory:"));
1202
1198
gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
1203
1199
gtk_table_attach (GTK_TABLE (table), label, 0, 1, 0, 1,
1204
1200
GTK_FILL, GTK_FILL, 0, 0);
1238
1234
1, 2, 2, 3, GTK_FILL, GTK_FILL, 0, 0);
1239
1235
gtk_widget_show (ifsOptD->subdivide_pair->spin);
1241
label = gtk_label_new (_("Spot Radius:"));
1237
label = gtk_label_new (_("Spot radius:"));
1242
1238
gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
1243
1239
gtk_table_attach (GTK_TABLE (table), label, 0, 1, 3, 4,
1244
1240
GTK_FILL, GTK_FILL, 0, 0);
1273
1269
GimpImageType type = gimp_drawable_type (drawable->drawable_id);
1274
1270
gint width = drawable->width;
1275
1271
gint height = drawable->height;
1276
gint num_bands, band_height, band_y, band_no;
1280
1278
guchar *mask = NULL;
1282
1280
guchar rc, gc, bc;
1285
num_bands = ceil((gdouble)(width*height*SQR(ifsvals.subdivide)*5)
1283
num_bands = ceil ((gdouble) (width * height * SQR (ifsvals.subdivide) * 5)
1286
1284
/ (1024 * ifsvals.max_memory));
1287
1285
band_height = (height + num_bands - 1) / num_bands;
1296
1294
gimp_context_get_background (&color);
1297
1295
gimp_rgb_get_uchar (&color, &rc, &gc, &bc);
1300
for (band_no = 0; band_no < num_bands; band_no++)
1298
for (band_no = 0, band_y = 0; band_no < num_bands; band_no++)
1307
GimpPixelRgn dest_rgn;
1313
buffer = g_strdup_printf (_("Rendering IFS (%d/%d)..."),
1314
band_no+1, num_bands);
1315
gimp_progress_init (buffer);
1300
GimpPixelRgn dest_rgn;
1303
gimp_progress_init_printf (_("Rendering IFS (%d/%d)"),
1304
band_no + 1, num_bands);
1318
1306
/* render the band to a buffer */
1319
1307
if (band_y + band_height > height)
1320
1308
band_height = height - band_y;
1322
1310
/* we don't need to clear data since we store nhits */
1323
memset(mask, 0, width*band_height*SQR(ifsvals.subdivide));
1324
memset(nhits, 0, width*band_height*SQR(ifsvals.subdivide));
1311
memset (mask, 0, width * band_height * SQR (ifsvals.subdivide));
1312
memset (nhits, 0, width * band_height * SQR (ifsvals.subdivide));
1326
1314
ifs_render (elements,
1327
1315
ifsvals.num_elements, width, height, ifsvals.iterations,
1330
1318
/* transfer the image to the drawable */
1333
buffer = g_strdup_printf (_("Copying IFS to image (%d/%d)..."),
1334
band_no+1, num_bands);
1335
gimp_progress_init (buffer);
1339
max_progress = band_height * width;
1341
1321
gimp_pixel_rgn_init (&dest_rgn, drawable, 0, band_y,
1342
1322
width, band_height, TRUE, TRUE);
1344
1325
for (pr = gimp_pixel_rgns_register (1, &dest_rgn);
1346
1327
pr = gimp_pixel_rgns_process (pr))
1348
destrow = dest_rgn.data;
1329
guchar *destrow = dest_rgn.data;
1350
1331
for (j = dest_rgn.y; j < (dest_rgn.y + dest_rgn.h); j++)
1333
guchar *dest = destrow;
1354
1335
for (i = dest_rgn.x; i < (dest_rgn.x + dest_rgn.w); i++)
1356
1337
/* Accumulate a reduced pixel */
1363
1345
for (jj = 0; jj < ifsvals.subdivide; jj++)
1366
(((j-band_y)*ifsvals.subdivide+jj) *
1367
ifsvals.subdivide*width +
1368
i*ifsvals.subdivide);
1351
3 * (((j - band_y) * ifsvals.subdivide + jj) *
1352
ifsvals.subdivide * width +
1353
i * ifsvals.subdivide);
1370
1355
maskptr = mask +
1371
((j-band_y)*ifsvals.subdivide+jj) *
1372
ifsvals.subdivide*width +
1373
i*ifsvals.subdivide;
1356
((j - band_y) * ifsvals.subdivide + jj) *
1357
ifsvals.subdivide * width +
1358
i * ifsvals.subdivide;
1374
1360
for (ii = 0; ii < ifsvals.subdivide; ii++)
1376
maskval = *maskptr++;
1362
guchar maskval = *maskptr++;
1377
1364
mtot += maskval;
1378
1365
rtot += maskval* *ptr++;
1379
1366
gtot += maskval* *ptr++;
1380
1367
btot += maskval* *ptr++;
1388
mtot /= SQR(ifsvals.subdivide);
1376
mtot /= SQR (ifsvals.subdivide);
1390
1379
/* and store it */
1393
1382
case GIMP_GRAY_IMAGE:
1394
*dest++ = (mtot*(rtot+btot+gtot)+
1395
(255-mtot)*(rc+gc+bc))/(3*255);
1383
*dest++ = (mtot * (rtot + btot + gtot) +
1384
(255 - mtot) * (rc + gc + bc)) / (3 * 255);
1397
1387
case GIMP_GRAYA_IMAGE:
1398
*dest++ = (rtot+btot+gtot)/3;
1388
*dest++ = (rtot + btot + gtot) / 3;
1399
1389
*dest++ = mtot;
1401
1392
case GIMP_RGB_IMAGE:
1402
*dest++ = (mtot*rtot + (255-mtot)*rc)/255;
1403
*dest++ = (mtot*gtot + (255-mtot)*gc)/255;
1404
*dest++ = (mtot*btot + (255-mtot)*bc)/255;
1393
*dest++ = (mtot * rtot + (255 - mtot) * rc) / 255;
1394
*dest++ = (mtot * gtot + (255 - mtot) * gc) / 255;
1395
*dest++ = (mtot * btot + (255 - mtot) * bc) / 255;
1406
1398
case GIMP_RGBA_IMAGE:
1407
1399
*dest++ = rtot;
1408
1400
*dest++ = gtot;
1409
1401
*dest++ = btot;
1410
1402
*dest++ = mtot;
1412
1405
case GIMP_INDEXED_IMAGE:
1413
1406
case GIMP_INDEXEDA_IMAGE:
1414
1407
g_error ("Indexed images not supported by IFS Fractal");
1418
1412
destrow += dest_rgn.rowstride;
1420
progress += dest_rgn.w * dest_rgn.h;
1421
gimp_progress_update ((gdouble) progress / (gdouble) max_progress);
1423
1416
band_y += band_height;
1488
1481
design_area_realize (GtkWidget *widget)
1483
const gint cursors[3] =
1485
GDK_FLEUR, /* OP_TRANSLATE */
1486
GDK_EXCHANGE, /* OP_ROTATE */
1487
GDK_CROSSHAIR /* OP_SHEAR */
1490
1490
GdkDisplay *display = gtk_widget_get_display (widget);
1491
GdkCursor *cursor = gdk_cursor_new_for_display (display, GDK_CROSSHAIR);
1491
GdkCursor *cursor = gdk_cursor_new_for_display (display,
1492
cursors[ifsDesign->op]);
1493
1493
gdk_window_set_cursor (widget->window, cursor);
1494
1494
gdk_cursor_unref (cursor);
2022
2022
FALSE, FALSE, 0);
2023
2023
gtk_widget_show (color_map->button);
2025
g_signal_connect (color_map->button, "color_changed",
2025
g_signal_connect (color_map->button, "color-changed",
2026
2026
G_CALLBACK (gimp_color_button_get_color),
2029
g_signal_connect (color_map->button, "color_changed",
2029
g_signal_connect (color_map->button, "color-changed",
2030
2030
G_CALLBACK (color_map_color_changed_cb),
2111
2111
value_pair->data.d = data;
2112
2112
value_pair->type = type;
2114
value_pair->adjustment =
2115
gtk_adjustment_new (1.0, lower, upper,
2116
(upper-lower) / 100, (upper-lower) / 10,
2118
/* We need to sink the adjustment, since we may not create a scale for
2119
* it, so nobody will assume the initial refcount
2121
g_object_ref (value_pair->adjustment);
2122
gtk_object_sink (value_pair->adjustment);
2114
value_pair->spin = gimp_spin_button_new (&value_pair->adjustment,
2116
(upper - lower) / 100,
2117
(upper - lower) / 10,
2119
gtk_widget_set_size_request (value_pair->spin, 72, -1);
2121
g_signal_connect (value_pair->adjustment, "value-changed",
2122
G_CALLBACK (value_pair_scale_callback),
2124
2125
if (create_scale)
2126
2127
value_pair->scale =
2127
2128
gtk_hscale_new (GTK_ADJUSTMENT (value_pair->adjustment));
2128
gtk_widget_ref (value_pair->scale);
2130
2130
if (type == VALUE_PAIR_INT)
2131
gtk_scale_set_digits (GTK_SCALE (value_pair->scale), 0);
2131
gtk_scale_set_digits (GTK_SCALE (value_pair->scale), 0);
2133
gtk_scale_set_digits (GTK_SCALE (value_pair->scale), 3);
2133
gtk_scale_set_digits (GTK_SCALE (value_pair->scale), 3);
2135
2135
gtk_scale_set_draw_value (GTK_SCALE (value_pair->scale), FALSE);
2136
2136
gtk_range_set_update_policy (GTK_RANGE (value_pair->scale),
2137
2137
GTK_UPDATE_DELAYED);
2140
value_pair->scale = NULL;
2142
/* We destroy the value pair when the spinbutton is destroyed, so
2143
* we don't need to hold a refcount on the entry
2147
= gtk_spin_button_new (GTK_ADJUSTMENT (value_pair->adjustment),
2149
gtk_widget_set_size_request (value_pair->spin, 72, -1);
2150
g_signal_connect (value_pair->spin, "destroy",
2151
G_CALLBACK (value_pair_destroy_callback),
2153
g_signal_connect (value_pair->adjustment, "value_changed",
2154
G_CALLBACK (value_pair_scale_callback),
2141
value_pair->scale = NULL;
2157
2144
return value_pair;