flame.c 36 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
/* flame - cosmic recursive fractal flames
 * Copyright (C) 1997  Scott Draves <spot@cs.cmu.edu>
 *
 * The GIMP -- an image manipulation program
 * Copyright (C) 1995 Spencer Kimball and Peter Mattis
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */
Elliot Lee's avatar
Elliot Lee committed
21

Tor Lillqvist's avatar
Tor Lillqvist committed
22 23
#include "config.h"

Elliot Lee's avatar
Elliot Lee committed
24
#include <errno.h>
25 26
#include <stdio.h>
#include <stdlib.h>
Elliot Lee's avatar
Elliot Lee committed
27 28 29
#include <string.h>
#include <time.h>

30 31 32
#include <libgimp/gimp.h>
#include <libgimp/gimpui.h>

33 34
#include "flame.h"

35
#include "libgimp/stdplugins-intl.h"
Elliot Lee's avatar
Elliot Lee committed
36 37


38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60
#define VARIATION_SAME    (-2)

#define HELP_ID           "plug-in-flame"

#define BUFFER_SIZE       10000

#define SCALE_WIDTH       150
#define PREVIEW_SIZE      150
#define EDIT_PREVIEW_SIZE 85
#define NMUTANTS          9

#define BLACK_DRAWABLE    (-2)
#define GRADIENT_DRAWABLE (-3)
#define TABLE_DRAWABLE    (-4)


struct
{
  gint          randomize;  /* superseded */
  gint          variation;
  gint32        cmap_drawable;
  control_point cp;
} config;
Elliot Lee's avatar
Elliot Lee committed
61

62

Elliot Lee's avatar
Elliot Lee committed
63
/* Declare local functions. */
64

65 66 67 68 69 70
static void      query             (void);
static void      run               (const gchar      *name,
                                    gint              nparams,
                                    const GimpParam  *param,
                                    gint             *nreturn_vals,
                                    GimpParam       **return_vals);
71
static void      flame             (GimpDrawable     *drawable);
72 73 74 75 76 77 78 79 80 81 82

static gboolean  dialog            (void);
static void      set_flame_preview (void);
static void      load_callback     (GtkWidget        *widget,
                                    gpointer          data);
static void      save_callback     (GtkWidget        *widget,
                                    gpointer          data);
static void      set_edit_preview  (void);
static void      combo_callback    (GtkWidget        *widget,
                                    gpointer          data);
static void      init_mutants      (void);
Elliot Lee's avatar
Elliot Lee committed
83

84 85 86 87 88 89 90 91 92 93

static gchar      buffer[BUFFER_SIZE];
static GtkWidget *cmap_preview;
static GtkWidget *flame_preview;
static gint       preview_width, preview_height;
static GtkWidget *dlg;
static GtkWidget *load_button = NULL;
static GtkWidget *save_button = NULL;
static GtkWidget *file_dlg = NULL;
static gint       load_save;
Elliot Lee's avatar
Elliot Lee committed
94

95
static GtkWidget *edit_dlg = NULL;
Elliot Lee's avatar
Elliot Lee committed
96

97 98 99 100
static control_point  edit_cp;
static control_point  mutants[NMUTANTS];
static GtkWidget     *edit_previews[NMUTANTS];
static gdouble        pick_speed = 0.2;
Elliot Lee's avatar
Elliot Lee committed
101

102 103
static frame_spec f = { 0.0, &config.cp, 1, 0.0 };

Elliot Lee's avatar
Elliot Lee committed
104

105
GimpPlugInInfo PLUG_IN_INFO =
Elliot Lee's avatar
Elliot Lee committed
106
{
107 108
  NULL,  /* init_proc  */
  NULL,  /* quit_proc  */
Elliot Lee's avatar
Elliot Lee committed
109
  query, /* query_proc */
110
  run,   /* run_proc   */
Elliot Lee's avatar
Elliot Lee committed
111 112 113
};


114
MAIN ()
Elliot Lee's avatar
Elliot Lee committed
115 116


117
static void
Sven Neumann's avatar
Sven Neumann committed
118
query (void)
Elliot Lee's avatar
Elliot Lee committed
119
{
120
  static GimpParamDef args[] =
Elliot Lee's avatar
Elliot Lee committed
121
  {
122
    { GIMP_PDB_INT32,    "run_mode", "Interactive, non-interactive" },
123 124
    { GIMP_PDB_IMAGE,    "image",    "Input image (unused)"         },
    { GIMP_PDB_DRAWABLE, "drawable", "Input drawable"               }
Elliot Lee's avatar
Elliot Lee committed
125
  };
126 127

  gimp_install_procedure ("plug_in_flame",
128 129 130 131 132 133 134 135 136 137
                          "Creates cosmic recursive fractal flames",
                          "Creates cosmic recursive fractal flames",
                          "Scott Draves",
                          "Scott Draves",
                          "1997",
                          N_("_Flame..."),
                          "RGB*",
                          GIMP_PLUGIN,
                          G_N_ELEMENTS (args), 0,
                          args, NULL);
138 139

  gimp_plugin_menu_register ("plug_in_flame",
140
                             "<Image>/Filters/Render/Nature");
Elliot Lee's avatar
Elliot Lee committed
141 142
}

143 144
static void
maybe_init_cp (void)
Sven Neumann's avatar
Sven Neumann committed
145
{
146 147
  if (0 == config.cp.spatial_oversample)
    {
148 149
      config.randomize     = 0;
      config.variation     = VARIATION_SAME;
150
      config.cmap_drawable = GRADIENT_DRAWABLE;
151 152 153 154 155 156 157 158 159 160

      random_control_point (&config.cp, variation_random);

      config.cp.center[0]             = 0.0;
      config.cp.center[1]             = 0.0;
      config.cp.pixels_per_unit       = 100;
      config.cp.spatial_oversample    = 2;
      config.cp.gamma                 = 2.0;
      config.cp.contrast              = 1.0;
      config.cp.brightness            = 1.0;
161
      config.cp.spatial_filter_radius = 0.75;
162 163 164 165 166
      config.cp.sample_density        = 5.0;
      config.cp.zoom                  = 0.0;
      config.cp.nbatches              = 1;
      config.cp.white_level           = 200;
      config.cp.cmap_index            = 72;
167
      /* cheating */
168 169
      config.cp.width                 = 256;
      config.cp.height                = 256;
170
    }
Elliot Lee's avatar
Elliot Lee committed
171 172
}

173 174 175 176
static void
run (const gchar      *name,
     gint              n_params,
     const GimpParam  *param,
177 178
     gint             *nreturn_vals,
     GimpParam       **return_vals)
Elliot Lee's avatar
Elliot Lee committed
179
{
180 181 182
  static GimpParam  values[1];
  GimpDrawable     *drawable = NULL;
  GimpRunMode       run_mode;
183
  GimpPDBStatusType status = GIMP_PDB_SUCCESS;
Elliot Lee's avatar
Elliot Lee committed
184 185 186 187 188

  *nreturn_vals = 1;
  *return_vals = values;

  run_mode = param[0].data.d_int32;
189

190 191
  INIT_I18N ();

192
  if (run_mode == GIMP_RUN_NONINTERACTIVE)
193
    {
194
      status = GIMP_PDB_CALLING_ERROR;
195 196 197 198 199 200 201
    }
  else
    {
      gimp_get_data ("plug_in_flame", &config);
      maybe_init_cp ();

      drawable = gimp_drawable_get (param[2].data.d_drawable);
202
      config.cp.width  = drawable->width;
203 204
      config.cp.height = drawable->height;

205
      if (run_mode == GIMP_RUN_INTERACTIVE)
206 207 208 209 210 211 212 213
        {
          if (! dialog ())
            {
              gimp_drawable_detach (drawable);

              status = GIMP_PDB_CANCEL;
            }
        }
214 215 216 217 218 219 220
      else
        {
          /*  reusing a drawable_ID from the last run is a bad idea
              since the drawable might have vanished  (bug #37761)   */
          if (config.cmap_drawable >= 0)
            config.cmap_drawable = GRADIENT_DRAWABLE;
        }
Elliot Lee's avatar
Elliot Lee committed
221 222
    }

223
  if (status == GIMP_PDB_SUCCESS)
224
    {
225
      if (gimp_drawable_is_rgb (drawable->drawable_id))
226 227 228 229
        {
          gimp_progress_init (_("Drawing Flame..."));
          gimp_tile_cache_ntiles (2 * (drawable->width /
                                       gimp_tile_width () + 1));
Elliot Lee's avatar
Elliot Lee committed
230

231
          flame (drawable);
Elliot Lee's avatar
Elliot Lee committed
232

233 234 235 236 237
          if (run_mode != GIMP_RUN_NONINTERACTIVE)
            gimp_displays_flush ();

          gimp_set_data ("plug_in_flame", &config, sizeof (config));
        }
238
      else
239 240 241 242
        {
          status = GIMP_PDB_EXECUTION_ERROR;
        }

243
      gimp_drawable_detach (drawable);
Elliot Lee's avatar
Elliot Lee committed
244 245
    }

246
  values[0].type          = GIMP_PDB_STATUS;
Elliot Lee's avatar
Elliot Lee committed
247 248 249 250
  values[0].data.d_status = status;
}

static void
251
drawable_to_cmap (control_point *cp)
Sven Neumann's avatar
Sven Neumann committed
252
{
253
  gint          i, j;
254 255
  GimpPixelRgn  pr;
  GimpDrawable *d;
256
  guchar       *p;
257 258 259 260 261 262 263 264 265

  if (TABLE_DRAWABLE >= config.cmap_drawable)
    {
      i = TABLE_DRAWABLE - config.cmap_drawable;
      get_cmap (i, cp->cmap, 256);
    }
  else if (BLACK_DRAWABLE == config.cmap_drawable)
    {
      for (i = 0; i < 256; i++)
266 267
        for (j = 0; j < 3; j++)
          cp->cmap[i][j] = 0.0;
268 269 270
    }
  else if (GRADIENT_DRAWABLE == config.cmap_drawable)
    {
271 272 273 274
      gchar   *name = gimp_context_get_gradient ();
      gint     num;
      gdouble *g;

275 276 277
#ifdef __GNUC__
#warning FIXME: "reverse" hardcoded to FALSE.
#endif
278 279 280 281 282
      gimp_gradient_get_uniform_samples (name, 256, FALSE,
                                         &num, &g);

      g_free (name);

283
      for (i = 0; i < 256; i++)
284 285
        for (j = 0; j < 3; j++)
          cp->cmap[i][j] = g[i*4 + j];
286 287 288 289 290 291 292
      g_free (g);
    }
  else
    {
      d = gimp_drawable_get (config.cmap_drawable);
      p = g_new (guchar, d->bpp);
      gimp_pixel_rgn_init (&pr, d, 0, 0,
293
                           d->width, d->height, FALSE, FALSE);
294
      for (i = 0; i < 256; i++)
295 296 297 298 299 300 301
        {
          gimp_pixel_rgn_get_pixel (&pr, p, i % d->width,
                                    (i / d->width) % d->height);
          for (j = 0; j < 3; j++)
            cp->cmap[i][j] =
              (d->bpp >= 3) ? (p[j] / 255.0) : (p[0]/255.0);
        }
302
      g_free (p);
Elliot Lee's avatar
Elliot Lee committed
303 304 305
    }
}

306
static void
307
flame (GimpDrawable *drawable)
Elliot Lee's avatar
Elliot Lee committed
308
{
309
  gint    width, height;
Elliot Lee's avatar
Elliot Lee committed
310
  guchar *tmp;
311
  gint    bytes;
Elliot Lee's avatar
Elliot Lee committed
312

313
  width  = drawable->width;
Elliot Lee's avatar
Elliot Lee committed
314
  height = drawable->height;
315
  bytes  = drawable->bpp;
Elliot Lee's avatar
Elliot Lee committed
316

317 318 319 320 321
  if (3 != bytes && 4 != bytes)
    {
      g_message (_("Flame works only on RGB drawables."));
      return;
    }
Elliot Lee's avatar
Elliot Lee committed
322

323
  tmp = g_new (guchar, width * height * 4);
Elliot Lee's avatar
Elliot Lee committed
324 325 326 327 328

  /* render */
  config.cp.width = width;
  config.cp.height = height;
  if (config.randomize)
329 330 331
    random_control_point (&config.cp, config.variation);
  drawable_to_cmap (&config.cp);
  render_rectangle (&f, tmp, width, field_both, 4,
332
                    gimp_progress_update);
Elliot Lee's avatar
Elliot Lee committed
333 334

  /* update destination */
335 336
  if (4 == bytes)
    {
337
      GimpPixelRgn pr;
338
      gimp_pixel_rgn_init (&pr, drawable, 0, 0, width, height,
339
                           TRUE, TRUE);
340 341 342 343 344
      gimp_pixel_rgn_set_rect (&pr, tmp, 0, 0, width, height);
    }
  else if (3 == bytes)
    {
      gint       i, j;
345
      GimpPixelRgn  src_pr, dst_pr;
346 347 348 349 350
      guchar    *sl;

      sl = g_new (guchar, 3 * width);

      gimp_pixel_rgn_init (&src_pr, drawable,
351
                           0, 0, width, height, FALSE, FALSE);
352
      gimp_pixel_rgn_init (&dst_pr, drawable,
353
                           0, 0, width, height, TRUE, TRUE);
354
      for (i = 0; i < height; i++)
355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375
        {
          guchar *rr = tmp + 4 * i * width;
          guchar *sld = sl;

          gimp_pixel_rgn_get_rect (&src_pr, sl, 0, i, width, 1);
          for (j = 0; j < width; j++)
            {
              gint k, alpha = rr[3];

              for (k = 0; k < 3; k++)
                {
                  gint t = (rr[k] + ((sld[k] * (256-alpha)) >> 8));

                  if (t > 255) t = 255;
                  sld[k] = t;
                }
              rr += 4;
              sld += 3;
            }
          gimp_pixel_rgn_set_rect (&dst_pr, sl, 0, i, width, 1);
        }
376
      g_free (sl);
Elliot Lee's avatar
Elliot Lee committed
377 378
    }

379 380
  g_free (tmp);
  gimp_drawable_flush (drawable);
381 382
  gimp_drawable_merge_shadow (drawable->drawable_id, TRUE);
  gimp_drawable_update (drawable->drawable_id, 0, 0, width, height);
Elliot Lee's avatar
Elliot Lee committed
383 384
}

385
static void
386 387 388
file_response_callback (GtkFileChooser *chooser,
                        gint            response_id,
                        gpointer        data)
Sven Neumann's avatar
Sven Neumann committed
389
{
390
  if (response_id == GTK_RESPONSE_OK)
391
    {
392
      gchar *filename = gtk_file_chooser_get_filename (chooser);
393 394 395 396 397 398 399 400 401

      if (load_save)
        {
          FILE  *f;
          gint   i, c;
          gchar *ss;

          if (!g_file_test (filename, G_FILE_TEST_IS_REGULAR))
            {
402 403
              g_message (_("'%s' is not a regular file"),
                         gimp_filename_to_utf8 (filename));
404
              g_free (filename);
405 406 407 408 409 410 411 412
              return;
            }

          f = fopen (filename, "r");

          if (f == NULL)
            {
              g_message (_("Could not open '%s' for reading: %s"),
413
                         gimp_filename_to_utf8 (filename), g_strerror (errno));
414
              g_free (filename);
415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443
              return;
            }

          i = 0;
          ss = buffer;
          do
            {
              c = getc (f);
              if (EOF == c)
                break;
              ss[i++] = c;
            }
          while (i < BUFFER_SIZE && ';' != c);
          parse_control_point (&ss, &config.cp);
          fclose (f);
          /* i want to update the existing dialogue, but it's
             too painful */
          gimp_set_data ("plug_in_flame", &config, sizeof (config));
          /* gtk_widget_destroy(dlg); */
          set_flame_preview ();
          set_edit_preview ();
        }
      else
        {
          FILE *f = fopen (filename, "w");

          if (NULL == f)
            {
              g_message (_("Could not open '%s' for writing: %s"),
444
                         gimp_filename_to_utf8 (filename), g_strerror (errno));
445
              g_free (filename);
446 447 448 449 450 451
              return;
            }

          print_control_point (f, &config.cp, 0);
          fclose (f);
        }
452 453

      g_free (filename);
Elliot Lee's avatar
Elliot Lee committed
454
    }
455

456
  gtk_widget_destroy (GTK_WIDGET (chooser));
457

458 459
  if (! GTK_WIDGET_SENSITIVE (load_button))
    gtk_widget_set_sensitive (load_button, TRUE);
Elliot Lee's avatar
Elliot Lee committed
460

461 462
  if (! GTK_WIDGET_SENSITIVE (save_button))
    gtk_widget_set_sensitive (save_button, TRUE);
Elliot Lee's avatar
Elliot Lee committed
463 464
}

465
static void
466 467
make_file_dlg (const gchar *title,
               GtkWidget   *parent)
Sven Neumann's avatar
Sven Neumann committed
468
{
469 470 471 472
  file_dlg = gtk_file_chooser_dialog_new (title, GTK_WINDOW (parent),
                                          load_save ?
                                          GTK_FILE_CHOOSER_ACTION_OPEN :
                                          GTK_FILE_CHOOSER_ACTION_SAVE,
473

474 475 476 477
                                          GTK_STOCK_CANCEL, GTK_STOCK_CANCEL,
                                          load_save ?
                                          GTK_STOCK_OPEN : GTK_STOCK_SAVE,
                                          GTK_RESPONSE_OK,
478

479 480
                                          NULL);

481
  g_object_add_weak_pointer (G_OBJECT (file_dlg), (gpointer) &file_dlg);
482 483 484

  gtk_dialog_set_default_response (GTK_DIALOG (file_dlg), GTK_RESPONSE_OK);
  gtk_window_set_destroy_with_parent (GTK_WINDOW (file_dlg), TRUE);
485 486 487 488 489 490 491

  g_signal_connect (file_dlg, "delete_event",
                    G_CALLBACK (gtk_true),
                    NULL);
  g_signal_connect (file_dlg, "response",
                    G_CALLBACK (file_response_callback),
                    NULL);
Elliot Lee's avatar
Elliot Lee committed
492 493
}

494 495
static void
randomize_callback (GtkWidget *widget,
496
                    gpointer   data)
Sven Neumann's avatar
Sven Neumann committed
497
{
498 499 500
  random_control_point (&edit_cp, config.variation);
  init_mutants ();
  set_edit_preview ();
Elliot Lee's avatar
Elliot Lee committed
501 502
}

503
static void
504 505 506
edit_response (GtkWidget *widget,
               gint       response_id,
               gpointer   data)
Sven Neumann's avatar
Sven Neumann committed
507
{
508 509 510 511 512 513 514
  gtk_widget_hide (widget);

  if (response_id == GTK_RESPONSE_OK)
    {
      config.cp = edit_cp;
      set_flame_preview ();
    }
Elliot Lee's avatar
Elliot Lee committed
515 516
}

517
static void
518
init_mutants (void)
Sven Neumann's avatar
Sven Neumann committed
519
{
520
  gint i;
Elliot Lee's avatar
Elliot Lee committed
521

522 523 524 525 526
  for (i = 0; i < NMUTANTS; i++)
    {
      mutants[i] = edit_cp;
      random_control_point (mutants + i, config.variation);
      if (VARIATION_SAME == config.variation)
527
        copy_variation (mutants + i, &edit_cp);
528
    }
Elliot Lee's avatar
Elliot Lee committed
529 530
}

531 532
static void
set_edit_preview (void)
Sven Neumann's avatar
Sven Neumann committed
533
{
Shlomi Fish's avatar
Shlomi Fish committed
534
  gint           i, j;
535 536 537
  guchar        *b;
  control_point  pcp;
  gint           nbytes = EDIT_PREVIEW_SIZE * EDIT_PREVIEW_SIZE * 3;
Elliot Lee's avatar
Elliot Lee committed
538

539
  static frame_spec pf = { 0.0, 0, 1, 0.0 };
Elliot Lee's avatar
Elliot Lee committed
540

541 542 543 544 545 546
  if (NULL == edit_previews[0])
    return;

  b = g_new (guchar, nbytes);
  maybe_init_cp ();
  drawable_to_cmap (&edit_cp);
Elliot Lee's avatar
Elliot Lee committed
547
  for (i = 0; i < 3; i++)
548 549
    for (j = 0; j < 3; j++)
      {
550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577
        gint mut = i*3 + j;

        pf.cps = &pcp;
        if (1 == i && 1 == j)
          {
            pcp = edit_cp;
          }
        else
          {
            control_point ends[2];
            ends[0] = edit_cp;
            ends[1] = mutants[mut];
            ends[0].time = 0.0;
            ends[1].time = 1.0;
            interpolate (ends, 2, pick_speed, &pcp);
          }
        pcp.pixels_per_unit =
          (pcp.pixels_per_unit * EDIT_PREVIEW_SIZE) / pcp.width;
        pcp.width = EDIT_PREVIEW_SIZE;
        pcp.height = EDIT_PREVIEW_SIZE;

        pcp.sample_density = 1;
        pcp.spatial_oversample = 1;
        pcp.spatial_filter_radius = 0.5;

        drawable_to_cmap (&pcp);

        render_rectangle (&pf, b, EDIT_PREVIEW_SIZE, field_both, 3, NULL);
578

Shlomi Fish's avatar
Shlomi Fish committed
579 580 581 582 583
        gimp_preview_area_draw (GIMP_PREVIEW_AREA (edit_previews[mut]),
                                0, 0, EDIT_PREVIEW_SIZE, EDIT_PREVIEW_SIZE,
                                GIMP_RGB_IMAGE,
                                b,
                                EDIT_PREVIEW_SIZE * 3);
Elliot Lee's avatar
Elliot Lee committed
584
      }
585
  g_free (b);
Elliot Lee's avatar
Elliot Lee committed
586 587
}

588
static void
Shlomi Fish's avatar
Shlomi Fish committed
589 590
edit_preview_size_allocate (GtkWidget *widget)
{
591
  set_edit_preview ();
Shlomi Fish's avatar
Shlomi Fish committed
592 593
}

594 595
static void
preview_clicked (GtkWidget *widget,
596
                 gpointer   data)
Sven Neumann's avatar
Sven Neumann committed
597
{
598
  gint mut = GPOINTER_TO_INT (data);
Elliot Lee's avatar
Elliot Lee committed
599

600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616
  if (mut == 4)
    {
      control_point t = edit_cp;
      init_mutants ();
      edit_cp = t;
    }
  else
    {
      control_point ends[2];
      ends[0] = edit_cp;
      ends[1] = mutants[mut];
      ends[0].time = 0.0;
      ends[1].time = 1.0;
      interpolate (ends, 2, pick_speed, &edit_cp);
    }
  set_edit_preview ();
}
Elliot Lee's avatar
Elliot Lee committed
617

618
static void
619
edit_callback (GtkWidget *widget,
620
               GtkWidget *parent)
Sven Neumann's avatar
Sven Neumann committed
621
{
Elliot Lee's avatar
Elliot Lee committed
622
  edit_cp = config.cp;
Sven Neumann's avatar
Sven Neumann committed
623

624
  if (edit_dlg == NULL)
Elliot Lee's avatar
Elliot Lee committed
625
    {
626 627 628 629
      GtkWidget *main_vbox;
      GtkWidget *frame;
      GtkWidget *table;
      GtkWidget *vbox;
Elliot Lee's avatar
Elliot Lee committed
630
      GtkWidget *hbox;
631
      GtkWidget *button;
632
      GtkWidget *combo;
633 634
      GtkWidget *label;
      GtkObject *adj;
635
      gint       i, j;
636 637

      edit_dlg = gimp_dialog_new (_("Edit Flame"), "flame",
638
                                  parent, GTK_DIALOG_DESTROY_WITH_PARENT,
639
                                  gimp_standard_help_func, HELP_ID,
640

641 642
                                  GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
                                  GTK_STOCK_OK,     GTK_RESPONSE_OK,
643

644
                                  NULL);
645

646 647 648
      g_signal_connect (edit_dlg, "response",
                        G_CALLBACK (edit_response),
                        edit_dlg);
649

650 651
      main_vbox = gtk_vbox_new (FALSE, 12);
      gtk_container_set_border_width (GTK_CONTAINER (main_vbox), 12);
652
      gtk_box_pack_start (GTK_BOX (GTK_DIALOG (edit_dlg)->vbox), main_vbox,
653
                          FALSE, FALSE, 0);
654

655
      frame = gimp_frame_new (_("Directions"));
656 657 658 659
      gtk_box_pack_start (GTK_BOX (main_vbox), frame, FALSE, FALSE, 0);
      gtk_widget_show (frame);

      table = gtk_table_new (3, 3, FALSE);
660 661
      gtk_table_set_row_spacings (GTK_TABLE (table), 6);
      gtk_table_set_col_spacings (GTK_TABLE (table), 6);
662 663 664 665
      gtk_container_add (GTK_CONTAINER (frame), table);
      gtk_widget_show (table);

      for (i = 0; i < 3; i++)
666 667 668
        for (j = 0; j < 3; j++)
          {
            gint mut = i * 3 + j;
669

670 671
            edit_previews[mut] = gimp_preview_area_new ();
            gtk_widget_set_size_request (edit_previews[mut],
Shlomi Fish's avatar
Shlomi Fish committed
672 673
                                         EDIT_PREVIEW_SIZE,
                                         EDIT_PREVIEW_SIZE);
674 675 676 677 678
            button = gtk_button_new ();
            gtk_container_add (GTK_CONTAINER(button), edit_previews[mut]);
            gtk_table_attach (GTK_TABLE (table), button, i, i+1, j, j+1,
                              GTK_EXPAND, GTK_EXPAND, 0, 0);
            gtk_widget_show (edit_previews[mut]);
Shlomi Fish's avatar
Shlomi Fish committed
679

680
            gtk_widget_show (button);
681

682
            g_signal_connect (button, "clicked",
683 684
                              G_CALLBACK (preview_clicked),
                              GINT_TO_POINTER (mut));
685 686
          }

Shlomi Fish's avatar
Shlomi Fish committed
687
      g_signal_connect (edit_previews[0], "size-allocate",
688 689
                        G_CALLBACK (edit_preview_size_allocate),
                        NULL);
690

691
      frame = gimp_frame_new (_("Controls"));
692 693 694
      gtk_box_pack_start (GTK_BOX (main_vbox), frame, FALSE, FALSE, 0);
      gtk_widget_show (frame);

695
      vbox = gtk_vbox_new (FALSE, 6);
696 697 698 699
      gtk_container_add (GTK_CONTAINER (frame), vbox);
      gtk_widget_show (vbox);

      table = gtk_table_new (1, 3, FALSE);
700
      gtk_table_set_col_spacings (GTK_TABLE (table), 6);
701 702 703 704
      gtk_box_pack_start (GTK_BOX (vbox), table, FALSE, FALSE, 0);
      gtk_widget_show(table);

      adj = gimp_scale_entry_new (GTK_TABLE (table), 0, 0,
705 706 707 708 709
                                  _("_Speed:"), SCALE_WIDTH, 0,
                                  pick_speed,
                                  0.05, 0.5, 0.01, 0.1, 2,
                                  TRUE, 0, 0,
                                  NULL, NULL);
710

711
      g_signal_connect (adj, "value_changed",
712 713
                        G_CALLBACK (gimp_double_adjustment_update),
                        &pick_speed);
714
      g_signal_connect (adj, "value_changed",
715 716
                        G_CALLBACK (set_edit_preview),
                        NULL);
Elliot Lee's avatar
Elliot Lee committed
717

718
      hbox = gtk_hbox_new (FALSE, 6);
719 720 721
      gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
      gtk_widget_show (hbox);

Maurits Rijk's avatar
Maurits Rijk committed
722
      button = gtk_button_new_with_mnemonic( _("_Randomize"));
723 724 725 726
      gtk_misc_set_padding (GTK_MISC (GTK_BIN (button)->child), 2, 0);
      gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
      gtk_widget_show (button);

727
      g_signal_connect_swapped (button, "clicked",
728 729 730
                                G_CALLBACK (randomize_callback),
                                NULL);

731 732 733 734 735 736 737 738 739 740
      combo = gimp_int_combo_box_new (_("Same"),       VARIATION_SAME,
                                      _("Random"),     variation_random,
                                      _("Linear"),     0,
                                      _("Sinusoidal"), 1,
                                      _("Spherical"),  2,
                                      _("Swirl"),      3,
                                      _("Horseshoe"),  4,
                                      _("Polar"),      5,
                                      _("Bent"),       6,
                                      NULL);
741

742 743
      gimp_int_combo_box_set_active (GIMP_INT_COMBO_BOX (combo),
                                     VARIATION_SAME);
744

745 746 747 748
      g_signal_connect (combo, "changed",
                        G_CALLBACK (combo_callback),
                        &config.variation);

749
      gtk_box_pack_end (GTK_BOX (hbox), combo, TRUE, TRUE, 0);
750
      gtk_widget_show (combo);
751

Maurits Rijk's avatar
Maurits Rijk committed
752
      label = gtk_label_new_with_mnemonic (_("_Variation:"));
753
      gtk_box_pack_end (GTK_BOX (hbox), label, FALSE, FALSE, 0);
754
      gtk_label_set_mnemonic_widget (GTK_LABEL (label), combo);
755 756 757 758 759
      gtk_widget_show (label);

      gtk_widget_show (main_vbox);

      init_mutants ();
Elliot Lee's avatar
Elliot Lee committed
760 761
    }

762 763
  set_edit_preview ();

764
  gtk_window_present (GTK_WINDOW (edit_dlg));
Elliot Lee's avatar
Elliot Lee committed
765 766
}

767 768
static void
load_callback (GtkWidget *widget,
769
               gpointer   data)
Sven Neumann's avatar
Sven Neumann committed
770
{
771
  if (! file_dlg)
772
    {
773 774 775 776
      load_save = 1;
      make_file_dlg (_("Load Flame"), gtk_widget_get_toplevel (widget));

      gtk_widget_set_sensitive (save_button, FALSE);
777
    }
778 779

  gtk_window_present (GTK_WINDOW (file_dlg));
Elliot Lee's avatar
Elliot Lee committed
780 781
}

782 783
static void
save_callback (GtkWidget *widget,
784
               gpointer   data)
Sven Neumann's avatar
Sven Neumann committed
785
{
786
  if (! file_dlg)
787
    {
788 789 790 791
      load_save = 0;
      make_file_dlg (_("Save Flame"), gtk_widget_get_toplevel (widget));

      gtk_widget_set_sensitive (load_button, FALSE);
792
    }
793 794

  gtk_window_present (GTK_WINDOW (file_dlg));
Elliot Lee's avatar
Elliot Lee committed
795 796
}

797
static void
798 799
combo_callback (GtkWidget *widget,
                gpointer   data)
Sven Neumann's avatar
Sven Neumann committed
800
{
801
  gimp_int_combo_box_get_active (GIMP_INT_COMBO_BOX (widget), (gint *) data);
802 803 804

  if (VARIATION_SAME != config.variation)
    random_control_point (&edit_cp, config.variation);
805

806 807
  init_mutants ();
  set_edit_preview ();
Elliot Lee's avatar
Elliot Lee committed
808 809
}

810 811
static void
set_flame_preview (void)
Sven Neumann's avatar
Sven Neumann committed
812
{
Elliot Lee's avatar
Elliot Lee committed
813 814
  guchar *b;
  control_point pcp;
815

Elliot Lee's avatar
Elliot Lee committed
816 817 818 819 820
  static frame_spec pf = {0.0, 0, 1, 0.0};

  if (NULL == flame_preview)
    return;

821
  b = g_new (guchar, preview_width * preview_height * 3);
Elliot Lee's avatar
Elliot Lee committed
822

823 824
  maybe_init_cp ();
  drawable_to_cmap (&config.cp);
Elliot Lee's avatar
Elliot Lee committed
825 826 827 828 829 830 831 832 833 834

  pf.cps = &pcp;
  pcp = config.cp;
  pcp.pixels_per_unit =
    (pcp.pixels_per_unit * preview_width) / pcp.width;
  pcp.width = preview_width;
  pcp.height = preview_height;
  pcp.sample_density = 1;
  pcp.spatial_oversample = 1;
  pcp.spatial_filter_radius = 0.1;
835
  render_rectangle (&pf, b, preview_width, field_both, 3, NULL);
Elliot Lee's avatar
Elliot Lee committed
836

Shlomi Fish's avatar
Shlomi Fish committed
837 838 839 840 841
  gimp_preview_area_draw (GIMP_PREVIEW_AREA (flame_preview),
                          0, 0, preview_width, PREVIEW_SIZE,
                          GIMP_RGB_IMAGE,
                          b,
                          preview_width * 3);
842
  g_free (b);
Shlomi Fish's avatar
Shlomi Fish committed
843
}
Elliot Lee's avatar
Elliot Lee committed
844

Shlomi Fish's avatar
Shlomi Fish committed
845 846 847
static void
flame_preview_size_allocate (GtkWidget *preview)
{
848
  set_flame_preview ();
Elliot Lee's avatar
Elliot Lee committed
849 850
}

851 852
static void
set_cmap_preview (void)
Sven Neumann's avatar
Sven Neumann committed
853
{
854
  gint i, x, y;
Elliot Lee's avatar
Elliot Lee committed
855
  guchar b[96];
Shlomi Fish's avatar
Shlomi Fish committed
856
  guchar *cmap_buffer, *ptr;
Elliot Lee's avatar
Elliot Lee committed
857 858 859 860

  if (NULL == cmap_preview)
    return;

861
  drawable_to_cmap (&config.cp);
Elliot Lee's avatar
Elliot Lee committed
862

863
  cmap_buffer = g_new (guchar, 32*32*3);
Shlomi Fish's avatar
Shlomi Fish committed
864
  ptr = cmap_buffer;
865 866 867
  for (y = 0; y < 32; y += 4)
    {
      for (x = 0; x < 32; x++)
868 869
        {
          gint j;
870

871 872 873 874
          i = x + (y/4) * 32;
          for (j = 0; j < 3; j++)
            b[x*3+j] = config.cp.cmap[i][j]*255.0;
        }
Elliot Lee's avatar
Elliot Lee committed
875

Shlomi Fish's avatar
Shlomi Fish committed
876 877 878 879 880 881 882 883
      memcpy (ptr, b, 32*3);
      ptr += 32*3;
      memcpy (ptr, b, 32*3);
      ptr += 32*3;
      memcpy (ptr, b, 32*3);
      ptr += 32*3;
      memcpy (ptr, b, 32*3);
      ptr += 32*3;
884 885
    }

Shlomi Fish's avatar
Shlomi Fish committed
886 887 888 889
  gimp_preview_area_draw (GIMP_PREVIEW_AREA (cmap_preview),
                          0, 0, 32, 32,
                          GIMP_RGB_IMAGE,
                          cmap_buffer,
890
                          32 * 3);
Shlomi Fish's avatar
Shlomi Fish committed
891
  g_free (cmap_buffer);
Elliot Lee's avatar
Elliot Lee committed
892 893
}

894
static void
895
cmap_callback (GtkWidget *widget,