home *** CD-ROM | disk | FTP | other *** search
/ PC Pro 2002 April / pcpro0402.iso / essentials / graphics / Gimp / gimp-src-20001226.exe / src / gimp / plug-ins / flame / flame.c < prev    next >
Encoding:
C/C++ Source or Header  |  2000-08-28  |  33.0 KB  |  1,241 lines

  1. /*
  2.    flame - cosmic recursive fractal flames
  3.    Copyright (C) 1997  Scott Draves <spot@cs.cmu.edu>
  4.  
  5.    The GIMP -- an image manipulation program
  6.    Copyright (C) 1995 Spencer Kimball and Peter Mattis
  7.  
  8.    This program is free software; you can redistribute it and/or modify
  9.    it under the terms of the GNU General Public License as published by
  10.    the Free Software Foundation; either version 2 of the License, or
  11.    (at your option) any later version.
  12.  
  13.    This program is distributed in the hope that it will be useful,
  14.    but WITHOUT ANY WARRANTY; without even the implied warranty of
  15.    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  16.    GNU General Public License for more details.
  17.  
  18.    You should have received a copy of the GNU General Public License
  19.    along with this program; if not, write to the Free Software
  20.    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  21. */
  22.  
  23. #include "config.h"
  24.  
  25. #include <stdlib.h>
  26. #include <stdio.h>
  27. #include <sys/types.h>
  28. #include <sys/stat.h>
  29. #ifdef HAVE_UNISTD_H
  30. #include <unistd.h>
  31. #endif
  32. #include <errno.h>
  33. #include <string.h>
  34. #include <time.h>
  35.  
  36. #include <gtk/gtk.h>
  37. #include <libgimp/gimp.h>
  38. #include <libgimp/gimpui.h>
  39.  
  40. #include "libgimp/stdplugins-intl.h"
  41.  
  42. #ifdef G_OS_WIN32
  43. #  include <io.h>
  44. #  ifndef S_ISREG
  45. #    define S_ISREG(m) ((m) & _S_IFREG)
  46. #  endif
  47. #endif
  48.  
  49. #include "flame.h"
  50.  
  51. #define VARIATION_SAME   (-2)
  52.  
  53. /* Declare local functions. */
  54. static void query             (void);
  55. static void run               (gchar   *name,
  56.                        gint     nparams,
  57.                        GimpParam  *param,
  58.                        gint    *nreturn_vals,
  59.                    GimpParam **return_vals);
  60. static void doit              (GimpDrawable *drawable);
  61.  
  62. static gint dialog            (void);
  63. static void set_flame_preview (void);
  64. static void load_callback     (GtkWidget *widget,
  65.                    gpointer   data);
  66. static void save_callback     (GtkWidget *widget,
  67.                    gpointer   data);
  68. static void set_edit_preview  (void);
  69. static void menu_cb           (GtkWidget *widget,
  70.                    gpointer   data);
  71. static void init_mutants      (void);
  72.  
  73. #define BUFFER_SIZE 10000
  74.  
  75. static gchar      buffer[BUFFER_SIZE];
  76. static GtkWidget *cmap_preview;
  77. static GtkWidget *flame_preview;
  78. static gint       preview_width, preview_height;
  79. static GtkWidget *dlg;
  80. static GtkWidget *load_button = NULL;
  81. static GtkWidget *save_button = NULL;
  82. static GtkWidget *file_dlg = NULL;
  83. static gint       load_save;
  84.  
  85. static GtkWidget *edit_dlg = NULL;
  86.  
  87. #define SCALE_WIDTH       150
  88. #define PREVIEW_SIZE      150
  89. #define EDIT_PREVIEW_SIZE 85
  90. #define NMUTANTS          9
  91.  
  92. static control_point  edit_cp;
  93. static control_point  mutants[NMUTANTS];
  94. static GtkWidget     *edit_previews[NMUTANTS];
  95. static gdouble        pick_speed = 0.2;
  96.  
  97.  
  98. GimpPlugInInfo PLUG_IN_INFO =
  99. {
  100.   NULL,  /* init_proc  */
  101.   NULL,  /* quit_proc  */
  102.   query, /* query_proc */
  103.   run,   /* run_proc   */
  104. };
  105.  
  106. static gint run_flag = FALSE;
  107.  
  108. #define BLACK_DRAWABLE    (-2)
  109. #define GRADIENT_DRAWABLE (-3)
  110. #define TABLE_DRAWABLE    (-4)
  111.  
  112.  
  113. struct
  114. {
  115.   gint          randomize;  /* superseded */
  116.   gint          variation;
  117.   gint32        cmap_drawable;
  118.   control_point cp;
  119. } config;
  120.  
  121. static frame_spec f = { 0.0, &config.cp, 1, 0.0 };
  122.  
  123.  
  124. MAIN ()
  125.  
  126.  
  127. static void 
  128. query (void)
  129. {
  130.   static GimpParamDef args[] =
  131.   {
  132.     { GIMP_PDB_INT32, "run_mode", "Interactive, non-interactive" },
  133.     { GIMP_PDB_IMAGE, "image", "Input image (unused)" },
  134.     { GIMP_PDB_DRAWABLE, "drawable", "Input drawable" },
  135.   };
  136.   static gint nargs = sizeof(args) / sizeof(args[0]);
  137.  
  138.   gimp_install_procedure ("plug_in_flame",
  139.               "Creates cosmic recursive fractal flames",
  140.               "Creates cosmic recursive fractal flames",
  141.               "Scott Draves",
  142.               "Scott Draves",
  143.               "1997",
  144.               N_("<Image>/Filters/Render/Nature/Flame..."),
  145.               "RGB*",
  146.               GIMP_PLUGIN,
  147.               nargs, 0,
  148.               args, NULL);
  149. }
  150.  
  151. static void 
  152. maybe_init_cp (void) 
  153. {
  154.   if (0 == config.cp.spatial_oversample)
  155.     {
  156.       config.randomize = 0;
  157.       config.variation = VARIATION_SAME;
  158.       config.cmap_drawable = GRADIENT_DRAWABLE;
  159.       random_control_point(&config.cp, variation_random);
  160.       config.cp.center[0] = 0.0;
  161.       config.cp.center[1] = 0.0;
  162.       config.cp.pixels_per_unit = 100;
  163.       config.cp.spatial_oversample = 2;
  164.       config.cp.gamma = 2.0;
  165.       config.cp.contrast = 1.0;
  166.       config.cp.brightness = 1.0;
  167.       config.cp.spatial_filter_radius = 0.75;
  168.       config.cp.sample_density = 5.0;
  169.       config.cp.zoom = 0.0;
  170.       config.cp.nbatches = 1;
  171.       config.cp.white_level = 200;
  172.       config.cp.cmap_index = 72;
  173.       /* cheating */
  174.       config.cp.width = 256;
  175.       config.cp.height = 256;
  176.     }
  177. }
  178.  
  179. static void 
  180. run (gchar   *name, 
  181.      gint     n_params, 
  182.      GimpParam  *param, 
  183.      gint    *nreturn_vals,
  184.      GimpParam **return_vals)
  185. {
  186.   static GimpParam values[1];
  187.   GimpDrawable *drawable = NULL;
  188.   GimpRunModeType run_mode;
  189.   GimpPDBStatusType status = GIMP_PDB_SUCCESS;
  190.  
  191.   *nreturn_vals = 1;
  192.   *return_vals = values;
  193.  
  194.   SRAND_FUNC (time (NULL));
  195.  
  196.   run_mode = param[0].data.d_int32;
  197.   
  198.   if (run_mode == GIMP_RUN_NONINTERACTIVE)
  199.     {
  200.       status = GIMP_PDB_CALLING_ERROR;
  201.     }
  202.   else
  203.     {
  204.       INIT_I18N_UI();
  205.       gimp_get_data ("plug_in_flame", &config);
  206.       maybe_init_cp ();
  207.  
  208.       drawable = gimp_drawable_get (param[2].data.d_drawable);
  209.       config.cp.width = drawable->width;
  210.       config.cp.height = drawable->height;
  211.  
  212.       if (run_mode == GIMP_RUN_INTERACTIVE)
  213.     {
  214.       if (!dialog ())
  215.         {
  216.           status = GIMP_PDB_EXECUTION_ERROR;
  217.         }
  218.     }
  219.     }
  220.  
  221.   if (status == GIMP_PDB_SUCCESS)
  222.     {
  223.       if (gimp_drawable_is_rgb (drawable->id))
  224.     {
  225.       gimp_progress_init (_("Drawing Flame..."));
  226.       gimp_tile_cache_ntiles (2 * (drawable->width /
  227.                        gimp_tile_width () + 1));
  228.  
  229.       doit (drawable);
  230.  
  231.       if (run_mode != GIMP_RUN_NONINTERACTIVE)
  232.         gimp_displays_flush ();
  233.       gimp_set_data ("plug_in_flame", &config, sizeof (config));
  234.     }
  235.       else
  236.     {
  237.       status = GIMP_PDB_EXECUTION_ERROR;
  238.     }
  239.       gimp_drawable_detach (drawable);
  240.     }
  241.  
  242.   values[0].type = GIMP_PDB_STATUS;
  243.   values[0].data.d_status = status;
  244. }
  245.  
  246. static void
  247. drawable_to_cmap (control_point *cp) 
  248. {
  249.   gint       i, j;
  250.   GimpPixelRgn  pr;
  251.   GimpDrawable *d;
  252.   guchar    *p;
  253.   gint       indexed;
  254.  
  255.   if (TABLE_DRAWABLE >= config.cmap_drawable)
  256.     {
  257.       i = TABLE_DRAWABLE - config.cmap_drawable;
  258.       get_cmap (i, cp->cmap, 256);
  259.     }
  260.   else if (BLACK_DRAWABLE == config.cmap_drawable)
  261.     {
  262.       for (i = 0; i < 256; i++)
  263.     for (j = 0; j < 3; j++)
  264.       cp->cmap[i][j] = 0.0;
  265.     }
  266.   else if (GRADIENT_DRAWABLE == config.cmap_drawable)
  267.     {
  268.       gdouble *g = gimp_gradients_sample_uniform (256);
  269.       for (i = 0; i < 256; i++)
  270.     for (j = 0; j < 3; j++)
  271.       cp->cmap[i][j] = g[i*4 + j];
  272.       g_free (g);
  273.     }
  274.   else
  275.     {
  276.       d = gimp_drawable_get (config.cmap_drawable);
  277.       indexed = gimp_drawable_is_indexed (config.cmap_drawable);
  278.       p = g_new (guchar, d->bpp);
  279.       gimp_pixel_rgn_init (&pr, d, 0, 0,
  280.                d->width, d->height, FALSE, FALSE);
  281.       for (i = 0; i < 256; i++)
  282.     {
  283.       gimp_pixel_rgn_get_pixel (&pr, p, i % d->width,
  284.                     (i / d->width) % d->height);
  285.       for (j = 0; j < 3; j++)
  286.         cp->cmap[i][j] =
  287.           (d->bpp >= 3) ? (p[j] / 255.0) : (p[0]/255.0);
  288.     }
  289.       g_free (p);
  290.     }
  291. }
  292.  
  293. static void 
  294. doit (GimpDrawable *drawable)
  295. {
  296.   gint    width, height;
  297.   guchar *tmp;
  298.   gint    bytes;
  299.  
  300.   width  = drawable->width;
  301.   height = drawable->height;
  302.   bytes  = drawable->bpp;
  303.  
  304.   if (3 != bytes && 4 != bytes)
  305.     {
  306.       g_message (_("Flame works only on RGB drawables."));
  307.       return;
  308.     }
  309.  
  310.   tmp = g_new (guchar, width * height * 4);
  311.  
  312.   /* render */
  313.   config.cp.width = width;
  314.   config.cp.height = height;
  315.   if (config.randomize)
  316.     random_control_point (&config.cp, config.variation);
  317.   drawable_to_cmap (&config.cp);
  318.   render_rectangle (&f, tmp, width, field_both, 4,
  319.             gimp_progress_update);
  320.  
  321.   /* update destination */
  322.   if (4 == bytes)
  323.     {
  324.       GimpPixelRgn pr;
  325.       gimp_pixel_rgn_init (&pr, drawable, 0, 0, width, height,
  326.                TRUE, TRUE);
  327.       gimp_pixel_rgn_set_rect (&pr, tmp, 0, 0, width, height);
  328.     }
  329.   else if (3 == bytes)
  330.     {
  331.       gint       i, j;
  332.       GimpPixelRgn  src_pr, dst_pr;
  333.       guchar    *sl;
  334.  
  335.       sl = g_new (guchar, 3 * width);
  336.  
  337.       gimp_pixel_rgn_init (&src_pr, drawable,
  338.                0, 0, width, height, FALSE, FALSE);
  339.       gimp_pixel_rgn_init (&dst_pr, drawable,
  340.                0, 0, width, height, TRUE, TRUE);
  341.       for (i = 0; i < height; i++)
  342.     {
  343.       guchar *rr = tmp + 4 * i * width;
  344.       guchar *sld = sl;
  345.  
  346.       gimp_pixel_rgn_get_rect (&src_pr, sl, 0, i, width, 1);
  347.       for (j = 0; j < width; j++)
  348.         {
  349.           gint k, alpha = rr[3];
  350.  
  351.           for (k = 0; k < 3; k++)
  352.         {
  353.           gint t = (rr[k] + ((sld[k] * (256-alpha)) >> 8));
  354.  
  355.           if (t > 255) t = 255;
  356.           sld[k] = t;
  357.         }
  358.           rr += 4;
  359.           sld += 3;
  360.         }
  361.       gimp_pixel_rgn_set_rect (&dst_pr, sl, 0, i, width, 1);
  362.     }
  363.       g_free (sl);
  364.     }
  365.  
  366.   g_free (tmp);
  367.   gimp_drawable_flush (drawable);
  368.   gimp_drawable_merge_shadow (drawable->id, TRUE);
  369.   gimp_drawable_update (drawable->id, 0, 0, width, height);
  370. }
  371.  
  372.  
  373. static void 
  374. ok_callback (GtkWidget *widget, 
  375.          gpointer   data)
  376. {
  377.   run_flag = TRUE;
  378.  
  379.   if (edit_dlg)
  380.     gtk_widget_destroy (edit_dlg);
  381.   gtk_widget_destroy (GTK_WIDGET (data));
  382. }
  383.  
  384. static void
  385. file_cancel_callback (GtkWidget *widget,
  386.               gpointer   data)
  387. {
  388.   gtk_widget_hide (widget);
  389.  
  390.   if (! GTK_WIDGET_SENSITIVE (load_button))
  391.     gtk_widget_set_sensitive (load_button, TRUE);
  392.  
  393.   if (! GTK_WIDGET_SENSITIVE (save_button))
  394.     gtk_widget_set_sensitive (save_button, TRUE);
  395. }
  396.  
  397. static void 
  398. file_ok_callback (GtkWidget *widget, 
  399.           gpointer   data) 
  400. {
  401.   GtkFileSelection *fs;
  402.   gchar            *filename;
  403.   struct stat       filestat;
  404.  
  405.   fs = GTK_FILE_SELECTION (data);
  406.   filename = gtk_file_selection_get_filename (fs);
  407.  
  408.   if (load_save)
  409.     {
  410.       FILE  *f;
  411.       gint   i, c;
  412.       gchar *ss;
  413.  
  414.       if (stat (filename, &filestat))
  415.     {
  416.       g_message ("%s: %s", filename, g_strerror (errno));
  417.       return;
  418.     }
  419.  
  420.       if (! S_ISREG (filestat.st_mode))
  421.     {
  422.       g_message (_("%s: Is not a regular file"), filename);
  423.       return;
  424.     }
  425.  
  426.       f = fopen (filename, "r");
  427.  
  428.       if (f == NULL)
  429.     {
  430.       g_message ("%s: %s", filename, g_strerror (errno));
  431.       return;
  432.     }
  433.  
  434.       i = 0;
  435.       ss = buffer;
  436.       do
  437.     {
  438.       c = getc (f);
  439.       if (EOF == c)
  440.         break;
  441.       ss[i++] = c;
  442.     }
  443.       while (i < BUFFER_SIZE && ';' != c);
  444.       parse_control_point (&ss, &config.cp);
  445.       fclose (f);
  446.       /* i want to update the existing dialogue, but it's
  447.      too painful */
  448.       gimp_set_data ("plug_in_flame", &config, sizeof (config));
  449.       /* gtk_widget_destroy(dlg); */
  450.       set_flame_preview ();
  451.       set_edit_preview ();
  452.     }
  453.   else
  454.     {
  455.       FILE *f = fopen (filename, "w");
  456.  
  457.       if (NULL == f)
  458.     {
  459.       g_message ("%s: %s", filename, g_strerror (errno));
  460.       return;
  461.     }
  462.  
  463.       print_control_point (f, &config.cp, 0);
  464.       fclose (f);
  465.     }
  466.  
  467.   file_cancel_callback (GTK_WIDGET (data), NULL);
  468. }
  469.  
  470. static void
  471. make_file_dlg (void) 
  472. {
  473.   file_dlg = gtk_file_selection_new (NULL);
  474.   gtk_quit_add_destroy (1, GTK_OBJECT (file_dlg));
  475.  
  476.   gtk_window_set_position (GTK_WINDOW (file_dlg), GTK_WIN_POS_MOUSE);
  477.   gtk_signal_connect_object (GTK_OBJECT (file_dlg), "delete_event",
  478.                  GTK_SIGNAL_FUNC (file_cancel_callback),
  479.                  GTK_OBJECT (file_dlg));
  480.   gtk_signal_connect (GTK_OBJECT (GTK_FILE_SELECTION (file_dlg)->ok_button),
  481.               "clicked",
  482.               GTK_SIGNAL_FUNC (file_ok_callback),
  483.               file_dlg);
  484.   gtk_signal_connect_object
  485.     (GTK_OBJECT (GTK_FILE_SELECTION (file_dlg)->cancel_button),
  486.      "clicked",
  487.      GTK_SIGNAL_FUNC (file_cancel_callback),
  488.      GTK_OBJECT (file_dlg));
  489.  
  490.   gimp_help_connect_help_accel (file_dlg,
  491.                 gimp_standard_help_func, "filters/flame.html");
  492. }
  493.  
  494. static void 
  495. randomize_callback (GtkWidget *widget, 
  496.             gpointer   data)
  497. {
  498.   random_control_point (&edit_cp, config.variation);
  499.   init_mutants ();
  500.   set_edit_preview ();
  501. }
  502.  
  503. static void 
  504. edit_ok_callback (GtkWidget *widget, 
  505.           gpointer   data)
  506. {
  507.   gtk_widget_hide (edit_dlg);
  508.   config.cp = edit_cp;
  509.   set_flame_preview ();  
  510. }
  511.  
  512. static void 
  513. init_mutants (void)
  514. {
  515.   gint i;
  516.  
  517.   for (i = 0; i < NMUTANTS; i++)
  518.     {
  519.       mutants[i] = edit_cp;
  520.       random_control_point (mutants + i, config.variation);
  521.       if (VARIATION_SAME == config.variation)
  522.     copy_variation (mutants + i, &edit_cp);
  523.     }
  524. }
  525.  
  526. static void 
  527. set_edit_preview (void) 
  528. {
  529.   gint           y, i, j;
  530.   guchar        *b;
  531.   control_point  pcp;
  532.   gint           nbytes = EDIT_PREVIEW_SIZE * EDIT_PREVIEW_SIZE * 3;
  533.  
  534.   static frame_spec pf = { 0.0, 0, 1, 0.0 };
  535.  
  536.   if (NULL == edit_previews[0])
  537.     return;
  538.  
  539.   b = g_new (guchar, nbytes);
  540.   maybe_init_cp ();
  541.   drawable_to_cmap (&edit_cp);
  542.   for (i = 0; i < 3; i++)
  543.     for (j = 0; j < 3; j++)
  544.       {
  545.     gint mut = i*3 + j;
  546.  
  547.     pf.cps = &pcp;
  548.     if (1 == i && 1 == j)
  549.       {
  550.         pcp = edit_cp;
  551.       }
  552.     else
  553.       {
  554.         control_point ends[2];
  555.         ends[0] = edit_cp;
  556.         ends[1] = mutants[mut];
  557.         ends[0].time = 0.0;
  558.         ends[1].time = 1.0;
  559.         interpolate (ends, 2, pick_speed, &pcp);
  560.       }
  561.     pcp.pixels_per_unit =
  562.       (pcp.pixels_per_unit * EDIT_PREVIEW_SIZE) / pcp.width;
  563.     pcp.width = EDIT_PREVIEW_SIZE;
  564.     pcp.height = EDIT_PREVIEW_SIZE;
  565.  
  566.     pcp.sample_density = 1;
  567.     pcp.spatial_oversample = 1;
  568.     pcp.spatial_filter_radius = 0.5;
  569.  
  570.     drawable_to_cmap (&pcp);
  571.  
  572.     render_rectangle (&pf, b, EDIT_PREVIEW_SIZE, field_both, 3, NULL);
  573.  
  574.     for (y = 0; y < EDIT_PREVIEW_SIZE; y++)
  575.       gtk_preview_draw_row (GTK_PREVIEW (edit_previews[mut]),
  576.                 b + y * EDIT_PREVIEW_SIZE * 3,
  577.                 0, y, EDIT_PREVIEW_SIZE);
  578.     gtk_widget_draw (edit_previews[mut], NULL);  
  579.       }
  580.   g_free (b);
  581. }
  582.  
  583. static void 
  584. preview_clicked (GtkWidget *widget, 
  585.          gpointer   data) 
  586. {
  587.   gint mut = (gint) data;
  588.  
  589.   if (mut == 4)
  590.     {
  591.       control_point t = edit_cp;
  592.       init_mutants ();
  593.       edit_cp = t;
  594.     }
  595.   else
  596.     {
  597.       control_point ends[2];
  598.       ends[0] = edit_cp;
  599.       ends[1] = mutants[mut];
  600.       ends[0].time = 0.0;
  601.       ends[1].time = 1.0;
  602.       interpolate (ends, 2, pick_speed, &edit_cp);
  603.     }
  604.   set_edit_preview ();
  605. }
  606.  
  607. static void
  608. edit_callback (GtkWidget *widget, 
  609.            gpointer   data) 
  610. {
  611.   edit_cp = config.cp;
  612.  
  613.   if (edit_dlg == NULL)
  614.     {
  615.       GtkWidget *main_vbox;
  616.       GtkWidget *frame;
  617.       GtkWidget *table;
  618.       GtkWidget *vbox;
  619.       GtkWidget *hbox;
  620.       GtkWidget *button;
  621.       GtkWidget *optionmenu;
  622.       GtkWidget *label;
  623.       GtkObject *adj;
  624.       gint i, j;
  625.  
  626.       edit_dlg = gimp_dialog_new (_("Edit Flame"), "flame",
  627.                   gimp_standard_help_func, "filters/flame.html",
  628.                   GTK_WIN_POS_MOUSE,
  629.                   FALSE, FALSE, FALSE,
  630.  
  631.                   _("OK"), edit_ok_callback,
  632.                   NULL, NULL, NULL, TRUE, FALSE,
  633.                   _("Cancel"), gtk_widget_hide,
  634.                   NULL, 1, NULL, FALSE, TRUE,
  635.  
  636.                   NULL);
  637.  
  638.       gtk_quit_add_destroy (1, GTK_OBJECT (edit_dlg));
  639.  
  640.       main_vbox = gtk_vbox_new (FALSE, 4);
  641.       gtk_container_set_border_width (GTK_CONTAINER (main_vbox), 6);
  642.       gtk_box_pack_start (GTK_BOX (GTK_DIALOG (edit_dlg)->vbox), main_vbox,
  643.               FALSE, FALSE, 0);
  644.  
  645.       frame = gtk_frame_new (_("Directions"));
  646.       gtk_box_pack_start (GTK_BOX (main_vbox), frame, FALSE, FALSE, 0);
  647.       gtk_widget_show (frame);
  648.  
  649.       table = gtk_table_new (3, 3, FALSE);
  650.       gtk_container_set_border_width (GTK_CONTAINER (table), 4);
  651.       gtk_table_set_row_spacings (GTK_TABLE (table), 4);
  652.       gtk_table_set_col_spacings (GTK_TABLE (table), 4);
  653.       gtk_container_add (GTK_CONTAINER (frame), table);
  654.       gtk_widget_show (table);
  655.  
  656.       for (i = 0; i < 3; i++)
  657.     for (j = 0; j < 3; j++)
  658.       {
  659.         gint mut = i * 3 + j;
  660.  
  661.         edit_previews[mut] = gtk_preview_new (GTK_PREVIEW_COLOR);
  662.         gtk_preview_size (GTK_PREVIEW (edit_previews[mut]),
  663.                   EDIT_PREVIEW_SIZE, EDIT_PREVIEW_SIZE);
  664.         button = gtk_button_new ();
  665.         gtk_container_add (GTK_CONTAINER(button), edit_previews[mut]);
  666.         gtk_signal_connect (GTK_OBJECT (button), "clicked",
  667.                 GTK_SIGNAL_FUNC (preview_clicked),
  668.                 (gpointer) mut);
  669.         gtk_table_attach (GTK_TABLE (table), button, i, i+1, j, j+1,
  670.                   GTK_EXPAND, GTK_EXPAND, 0, 0);
  671.         gtk_widget_show (edit_previews[mut]);
  672.         gtk_widget_show (button);
  673.       }
  674.  
  675.       frame = gtk_frame_new( _("Controls"));
  676.       gtk_box_pack_start (GTK_BOX (main_vbox), frame, FALSE, FALSE, 0);
  677.       gtk_widget_show (frame);
  678.  
  679.       vbox = gtk_vbox_new (FALSE, 4);
  680.       gtk_container_set_border_width (GTK_CONTAINER (vbox), 4);
  681.       gtk_container_add (GTK_CONTAINER (frame), vbox);
  682.       gtk_widget_show (vbox);
  683.  
  684.       table = gtk_table_new (1, 3, FALSE);
  685.       gtk_table_set_col_spacings (GTK_TABLE (table), 4);
  686.       gtk_box_pack_start (GTK_BOX (vbox), table, FALSE, FALSE, 0);
  687.       gtk_widget_show(table);
  688.  
  689.       adj = gimp_scale_entry_new (GTK_TABLE (table), 0, 0,
  690.                   _("Speed:"), SCALE_WIDTH, 0,
  691.                   pick_speed,
  692.                   0.05, 0.5, 0.01, 0.1, 2,
  693.                   TRUE, 0, 0,
  694.                   NULL, NULL);
  695.       gtk_signal_connect (GTK_OBJECT (adj), "value_changed",
  696.               GTK_SIGNAL_FUNC (gimp_double_adjustment_update),
  697.               &pick_speed);
  698.       gtk_signal_connect (GTK_OBJECT (adj), "value_changed",
  699.               GTK_SIGNAL_FUNC (set_edit_preview),
  700.               NULL);
  701.  
  702.       hbox = gtk_hbox_new (FALSE, 4);
  703.       gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
  704.       gtk_widget_show (hbox);
  705.  
  706.       button = gtk_button_new_with_label( _("Randomize"));
  707.       gtk_misc_set_padding (GTK_MISC (GTK_BIN (button)->child), 2, 0);
  708.       gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
  709.       gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
  710.                  GTK_SIGNAL_FUNC (randomize_callback),
  711.                  NULL);
  712.       gtk_widget_show (button);
  713.  
  714.       optionmenu =
  715.     gimp_option_menu_new2 (FALSE, menu_cb,
  716.                    &config.variation,
  717.                    (gpointer) VARIATION_SAME,
  718.  
  719.                    _("Same"),     (gpointer) VARIATION_SAME, NULL,
  720.                    _("Random"),   (gpointer) variation_random, NULL,
  721.                    _("Linear"),     (gpointer) 0, NULL,
  722.                    _("Sinusoidal"), (gpointer) 1, NULL,
  723.                    _("Spherical"),  (gpointer) 2, NULL,
  724.                    _("Swirl"),      (gpointer) 3, NULL,
  725.                    _("Horseshoe"),  (gpointer) 4, NULL,
  726.                    _("Polar"),      (gpointer) 5, NULL,
  727.                    _("Bent"),       (gpointer) 6, NULL,
  728.  
  729.                    NULL);
  730.       gtk_box_pack_end (GTK_BOX (hbox), optionmenu, FALSE, FALSE, 0);
  731.       gtk_widget_show (optionmenu);
  732.  
  733.       label = gtk_label_new (_("Variation:"));
  734.       gtk_box_pack_end (GTK_BOX (hbox), label, FALSE, FALSE, 0);
  735.       gtk_widget_show (label);
  736.  
  737.       gtk_widget_show (main_vbox);
  738.  
  739.       init_mutants ();
  740.     }
  741.  
  742.   set_edit_preview ();
  743.  
  744.   if (!GTK_WIDGET_VISIBLE (edit_dlg))
  745.     gtk_widget_show (edit_dlg);
  746.   else
  747.     gdk_window_raise (edit_dlg->window);
  748. }
  749.  
  750. static void 
  751. load_callback (GtkWidget *widget, 
  752.            gpointer   data) 
  753. {
  754.   if (!file_dlg)
  755.     {
  756.       make_file_dlg ();
  757.     }
  758.   else
  759.     {
  760.       if (GTK_WIDGET_VISIBLE (file_dlg))
  761.     {
  762.       gdk_window_raise (file_dlg->window);
  763.       return;
  764.     }
  765.     }
  766.   gtk_window_set_title (GTK_WINDOW (file_dlg), _("Load Flame"));
  767.   load_save = 1;
  768.   gtk_widget_set_sensitive (save_button, FALSE);
  769.   gtk_widget_show (file_dlg);
  770. }
  771.  
  772. static void 
  773. save_callback (GtkWidget *widget, 
  774.            gpointer   data) 
  775. {
  776.   if (!file_dlg)
  777.     {
  778.       make_file_dlg ();
  779.     }
  780.   else
  781.     {
  782.       if (GTK_WIDGET_VISIBLE (file_dlg))
  783.     {
  784.       gdk_window_raise (file_dlg->window);
  785.       return;
  786.     }
  787.     }
  788.   gtk_window_set_title (GTK_WINDOW (file_dlg), _("Save Flame"));
  789.   load_save = 0;
  790.   gtk_widget_set_sensitive (load_button, FALSE);
  791.   gtk_widget_show (file_dlg);
  792. }
  793.  
  794. static void 
  795. menu_cb (GtkWidget *widget, 
  796.      gpointer   data) 
  797. {
  798.   gimp_menu_item_update (widget, data);
  799.  
  800.   if (VARIATION_SAME != config.variation)
  801.     random_control_point (&edit_cp, config.variation);
  802.   init_mutants ();
  803.   set_edit_preview ();
  804. }
  805.  
  806. static void 
  807. set_flame_preview (void) 
  808. {
  809.   gint    y;
  810.   guchar *b;
  811.   control_point pcp;
  812.  
  813.   static frame_spec pf = {0.0, 0, 1, 0.0};
  814.  
  815.   if (NULL == flame_preview)
  816.     return;
  817.  
  818.   b = g_new (guchar, preview_width * preview_height * 3);
  819.  
  820.   maybe_init_cp ();
  821.   drawable_to_cmap (&config.cp);
  822.  
  823.   pf.cps = &pcp;
  824.   pcp = config.cp;
  825.   pcp.pixels_per_unit =
  826.     (pcp.pixels_per_unit * preview_width) / pcp.width;
  827.   pcp.width = preview_width;
  828.   pcp.height = preview_height;
  829.   pcp.sample_density = 1;
  830.   pcp.spatial_oversample = 1;
  831.   pcp.spatial_filter_radius = 0.1;
  832.   render_rectangle (&pf, b, preview_width, field_both, 3, NULL);
  833.  
  834.   for (y = 0; y < PREVIEW_SIZE; y++)
  835.     gtk_preview_draw_row (GTK_PREVIEW (flame_preview),
  836.               b+y*preview_width*3, 0, y, preview_width);
  837.   g_free (b);
  838.  
  839.   gtk_widget_draw (flame_preview, NULL);
  840. }
  841.  
  842. static void 
  843. set_cmap_preview (void) 
  844. {
  845.   gint i, x, y;
  846.   guchar b[96];
  847.  
  848.   if (NULL == cmap_preview)
  849.     return;
  850.  
  851.   drawable_to_cmap (&config.cp);
  852.  
  853.   for (y = 0; y < 32; y += 4)
  854.     {
  855.       for (x = 0; x < 32; x++)
  856.     {
  857.       gint j;
  858.  
  859.       i = x + (y/4) * 32;
  860.       for (j = 0; j < 3; j++)
  861.         b[x*3+j] = config.cp.cmap[i][j]*255.0;
  862.     }
  863.     gtk_preview_draw_row (GTK_PREVIEW (cmap_preview), b, 0, y, 32);
  864.     gtk_preview_draw_row (GTK_PREVIEW (cmap_preview), b, 0, y+1, 32);
  865.     gtk_preview_draw_row (GTK_PREVIEW (cmap_preview), b, 0, y+2, 32);
  866.     gtk_preview_draw_row (GTK_PREVIEW (cmap_preview), b, 0, y+3, 32);
  867.   }
  868.  
  869.   gtk_widget_draw (cmap_preview, NULL);  
  870. }
  871.  
  872. static void 
  873. gradient_cb (GtkWidget *widget, 
  874.          gpointer   data) 
  875. {
  876.   config.cmap_drawable = (gint) data;
  877.   set_cmap_preview();
  878.   set_flame_preview();
  879.   /* set_edit_preview(); */
  880. }
  881.  
  882. static void 
  883. cmap_callback (gint32   id, 
  884.            gpointer data) 
  885. {
  886.   config.cmap_drawable = id;
  887.   set_cmap_preview();
  888.   set_flame_preview();
  889.   /* set_edit_preview(); */
  890. }
  891.  
  892. static gint
  893. cmap_constrain (gint32   image_id, 
  894.         gint32   drawable_id, 
  895.         gpointer data) 
  896. {  
  897.   return ! gimp_drawable_is_indexed (drawable_id);
  898. }
  899.  
  900.  
  901. static gint 
  902. dialog (void) 
  903. {
  904.   GtkWidget *main_vbox;
  905.   GtkWidget *frame;
  906.   GtkWidget *abox;
  907.   GtkWidget *pframe;
  908.   GtkWidget *button;
  909.   GtkWidget *table;
  910.   GtkWidget *box;
  911.   GtkObject *adj;
  912.  
  913.   gimp_ui_init ("flame", TRUE);
  914.  
  915.   dlg = gimp_dialog_new (_("Flame"), "flame",
  916.              gimp_standard_help_func, "filters/flame.html",
  917.              GTK_WIN_POS_MOUSE,
  918.              FALSE, TRUE, FALSE,
  919.  
  920.              _("OK"), ok_callback,
  921.              NULL, NULL, NULL, TRUE, FALSE,
  922.              _("Cancel"), gtk_widget_destroy,
  923.              NULL, 1, NULL, FALSE, TRUE,
  924.  
  925.              NULL);
  926.   gtk_signal_connect (GTK_OBJECT (dlg), "destroy",
  927.               GTK_SIGNAL_FUNC (gtk_main_quit),
  928.               NULL);
  929.  
  930.   main_vbox = gtk_vbox_new (FALSE, 4);
  931.   gtk_container_set_border_width (GTK_CONTAINER (main_vbox), 6);
  932.   gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dlg)->vbox), main_vbox,
  933.               FALSE, FALSE, 0);
  934.   gtk_widget_show (main_vbox);
  935.  
  936.   box = gtk_hbox_new (FALSE, 6);
  937.   gtk_box_pack_start (GTK_BOX (main_vbox), box, FALSE, FALSE, 0);
  938.   gtk_widget_show (box);
  939.  
  940.   frame = gtk_frame_new (_("Preview"));
  941.   gtk_box_pack_start (GTK_BOX (box), frame, TRUE, TRUE, 0);
  942.   gtk_widget_show (frame);
  943.  
  944.   abox = gtk_alignment_new (0.5, 0.5, 0.0, 0.0);
  945.   gtk_container_set_border_width (GTK_CONTAINER (abox), 4);
  946.   gtk_container_add (GTK_CONTAINER (frame), abox);
  947.   gtk_widget_show (abox);
  948.  
  949.   pframe = gtk_frame_new (NULL);
  950.   gtk_frame_set_shadow_type (GTK_FRAME (pframe), GTK_SHADOW_IN);
  951.   gtk_container_add (GTK_CONTAINER (abox), pframe);
  952.   gtk_widget_show (pframe);
  953.  
  954.   frame = gtk_frame_new (_("Flame"));
  955.   gtk_box_pack_start (GTK_BOX (box), frame, FALSE, FALSE, 0);
  956.   gtk_widget_show (frame);
  957.  
  958.   {
  959.     GtkWidget *vbbox;
  960.  
  961.     box = gtk_vbox_new (FALSE, 0);
  962.     gtk_container_set_border_width (GTK_CONTAINER (box), 4);
  963.     gtk_container_add (GTK_CONTAINER (frame), box);
  964.     gtk_widget_show (box);
  965.  
  966.     vbbox= gtk_vbutton_box_new ();
  967.     gtk_box_set_homogeneous (GTK_BOX (vbbox), FALSE);
  968.     gtk_button_box_set_spacing (GTK_BUTTON_BOX (vbbox), 4);
  969.     gtk_box_pack_start (GTK_BOX (box), vbbox, FALSE, FALSE, 0);
  970.     gtk_widget_show (vbbox);
  971.   
  972.     button = gtk_button_new_with_label (_("Edit Flame"));
  973.     gtk_signal_connect (GTK_OBJECT (button), "clicked",
  974.             GTK_SIGNAL_FUNC (edit_callback),
  975.             NULL);
  976.     gtk_box_pack_start (GTK_BOX (vbbox), button, FALSE, FALSE, 0);
  977.     gtk_widget_show (button);
  978.  
  979.     load_button = button = gtk_button_new_with_label (_("Load Flame"));
  980.     gtk_signal_connect (GTK_OBJECT (button), "clicked",
  981.             GTK_SIGNAL_FUNC (load_callback),
  982.             NULL);
  983.     gtk_box_pack_start (GTK_BOX (vbbox), button, FALSE, FALSE, 0);
  984.     gtk_widget_show (button);
  985.  
  986.     save_button = button = gtk_button_new_with_label (_("Save Flame"));
  987.     gtk_signal_connect (GTK_OBJECT (button), "clicked",
  988.             GTK_SIGNAL_FUNC (save_callback),
  989.             NULL);
  990.     gtk_box_pack_start (GTK_BOX (vbbox), button, FALSE, FALSE, 0);
  991.     gtk_widget_show (button);
  992.   }
  993.  
  994.   frame = gtk_frame_new (_("Rendering"));
  995.   gtk_box_pack_start (GTK_BOX (main_vbox), frame, FALSE, FALSE, 0);
  996.   gtk_widget_show (frame);
  997.  
  998.   box = gtk_vbox_new (FALSE, 4);
  999.   gtk_container_set_border_width (GTK_CONTAINER (box), 4);
  1000.   gtk_container_add (GTK_CONTAINER (frame), box);
  1001.   gtk_widget_show (box);
  1002.  
  1003.   table = gtk_table_new (6, 3, FALSE);
  1004.   gtk_table_set_row_spacings (GTK_TABLE (table), 2);
  1005.   gtk_table_set_row_spacing (GTK_TABLE (table), 2, 6);
  1006.   gtk_table_set_col_spacings (GTK_TABLE (table), 4);
  1007.   gtk_box_pack_start (GTK_BOX (box), table, FALSE, FALSE, 0);
  1008.   gtk_widget_show (table);
  1009.  
  1010.   adj = gimp_scale_entry_new (GTK_TABLE (table), 0, 0,
  1011.                   _("Brightness:"), SCALE_WIDTH, 0,
  1012.                   config.cp.brightness,
  1013.                   0, 5, 0.1, 1, 2,
  1014.                   TRUE, 0, 0,
  1015.                   NULL, NULL);
  1016.   gtk_signal_connect (GTK_OBJECT (adj), "value_changed",
  1017.               GTK_SIGNAL_FUNC (gimp_double_adjustment_update),
  1018.               &config.cp.brightness);
  1019.   gtk_signal_connect (GTK_OBJECT (adj), "value_changed",
  1020.               GTK_SIGNAL_FUNC (set_flame_preview),
  1021.               NULL);
  1022.  
  1023.   adj = gimp_scale_entry_new (GTK_TABLE (table), 0, 1,
  1024.                   _("Contrast:"), SCALE_WIDTH, 0,
  1025.                   config.cp.contrast,
  1026.                   0, 5, 0.1, 1, 2,
  1027.                   TRUE, 0, 0,
  1028.                   NULL, NULL);
  1029.   gtk_signal_connect (GTK_OBJECT (adj), "value_changed",
  1030.               GTK_SIGNAL_FUNC (gimp_double_adjustment_update),
  1031.               &config.cp.contrast);
  1032.   gtk_signal_connect (GTK_OBJECT (adj), "value_changed",
  1033.               GTK_SIGNAL_FUNC (set_flame_preview),
  1034.               NULL);
  1035.  
  1036.   adj = gimp_scale_entry_new (GTK_TABLE (table), 0, 2,
  1037.                   _("Gamma:"), SCALE_WIDTH, 0,
  1038.                   config.cp.gamma,
  1039.                   1, 5, 0.1, 1, 2,
  1040.                   TRUE, 0, 0,
  1041.                   NULL, NULL);
  1042.   gtk_signal_connect (GTK_OBJECT (adj), "value_changed",
  1043.               GTK_SIGNAL_FUNC (gimp_double_adjustment_update),
  1044.               &config.cp.gamma);
  1045.   gtk_signal_connect (GTK_OBJECT (adj), "value_changed",
  1046.               GTK_SIGNAL_FUNC (set_flame_preview),
  1047.               NULL);
  1048.  
  1049.   adj = gimp_scale_entry_new (GTK_TABLE (table), 0, 3,
  1050.                   _("Sample Density:"), SCALE_WIDTH, 0,
  1051.                   config.cp.sample_density,
  1052.                   0.1, 20, 1, 5, 2,
  1053.                   TRUE, 0, 0,
  1054.                   NULL, NULL);
  1055.   gtk_signal_connect (GTK_OBJECT (adj), "value_changed",
  1056.               GTK_SIGNAL_FUNC (gimp_double_adjustment_update),
  1057.               &config.cp.sample_density);
  1058.  
  1059.   adj = gimp_scale_entry_new (GTK_TABLE (table), 0, 4,
  1060.                   _("Spatial Oversample:"), SCALE_WIDTH, 0,
  1061.                   config.cp.spatial_oversample,
  1062.                   0, 1, 0.01, 0.1, 0,
  1063.                   TRUE, 0, 0,
  1064.                   NULL, NULL);
  1065.   gtk_signal_connect (GTK_OBJECT (adj), "value_changed",
  1066.               GTK_SIGNAL_FUNC (gimp_int_adjustment_update),
  1067.               &config.cp.spatial_oversample);
  1068.  
  1069.   adj = gimp_scale_entry_new (GTK_TABLE (table), 0, 5,
  1070.                   _("Spatial Filter Radius:"), SCALE_WIDTH, 0,
  1071.                   config.cp.spatial_filter_radius,
  1072.                   0, 4, 0.2, 1, 2,
  1073.                   TRUE, 0, 0,
  1074.                   NULL, NULL);
  1075.   gtk_signal_connect (GTK_OBJECT (adj), "value_changed",
  1076.               GTK_SIGNAL_FUNC (gimp_double_adjustment_update),
  1077.               &config.cp.spatial_filter_radius);
  1078.  
  1079.   {
  1080.     GtkWidget *sep;
  1081.     GtkWidget *menu;
  1082.     GtkWidget *hbox;
  1083.     GtkWidget *label;
  1084.     GtkWidget *menuitem;
  1085.     GtkWidget *option_menu;
  1086.     gint32 save_drawable = config.cmap_drawable;
  1087.  
  1088.     sep = gtk_hseparator_new ();
  1089.     gtk_box_pack_start (GTK_BOX (box), sep, FALSE, FALSE, 0);
  1090.     gtk_widget_show (sep);
  1091.  
  1092.     hbox = gtk_hbox_new (FALSE, 4);
  1093.     gtk_box_pack_start (GTK_BOX (box), hbox, FALSE, FALSE, 0);
  1094.     gtk_widget_show (hbox);
  1095.  
  1096.     label = gtk_label_new (_("Colormap:"));
  1097.     gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
  1098.     gtk_widget_show (label);
  1099.  
  1100.     option_menu = gtk_option_menu_new ();
  1101.     gtk_box_pack_start (GTK_BOX (hbox), option_menu, FALSE, FALSE, 0);
  1102.     menu = gimp_drawable_menu_new (cmap_constrain, cmap_callback,
  1103.                    0, config.cmap_drawable);
  1104.  
  1105.     config.cmap_drawable = save_drawable;
  1106. #if 0
  1107.     menuitem = gtk_menu_item_new_with_label (_("Black"));
  1108.     gtk_signal_connect (GTK_OBJECT (menuitem), "activate",
  1109.             GTK_SIGNAL_FUNC (gradient_cb),
  1110.             (gpointer) BLACK_DRAWABLE);
  1111.     gtk_menu_prepend (GTK_MENU (menu), menuitem);
  1112.     if (BLACK_DRAWABLE == save_drawable)
  1113.       gtk_menu_set_active (GTK_MENU (menu), 0);
  1114.     gtk_widget_show (menuitem); 
  1115. #endif
  1116.     {
  1117.       static gchar *names[] =
  1118.       {
  1119.     "sunny harvest",
  1120.     "rose",
  1121.     "calcoast09",
  1122.     "klee insula-dulcamara",
  1123.     "ernst anti-pope",
  1124.     "gris josette"
  1125.       };
  1126.       static gint good[] = { 10, 20, 68, 79, 70, 75 };
  1127.       gint i, n = (sizeof good) / (sizeof good[0]);
  1128.  
  1129.       for (i = 0; i < n; i++)
  1130.     {
  1131.       gint d = TABLE_DRAWABLE - good[i];
  1132.  
  1133.       menuitem = gtk_menu_item_new_with_label (names[i]);
  1134.       gtk_signal_connect (GTK_OBJECT (menuitem), "activate",
  1135.                   GTK_SIGNAL_FUNC (gradient_cb),
  1136.                   (gpointer) d);
  1137.       gtk_menu_prepend (GTK_MENU (menu), menuitem);
  1138.       if (d == save_drawable)
  1139.         gtk_menu_set_active (GTK_MENU (menu), 0);
  1140.       gtk_widget_show (menuitem);
  1141.     }
  1142.     }
  1143.  
  1144.     menuitem = gtk_menu_item_new_with_label (_("Custom Gradient"));
  1145.     gtk_signal_connect (GTK_OBJECT (menuitem), "activate",
  1146.             GTK_SIGNAL_FUNC (gradient_cb),
  1147.             (gpointer) GRADIENT_DRAWABLE);
  1148.     gtk_menu_prepend (GTK_MENU (menu), menuitem);
  1149.     if (GRADIENT_DRAWABLE == save_drawable)
  1150.       gtk_menu_set_active (GTK_MENU (menu), 0);
  1151.     gtk_widget_show (menuitem);
  1152.  
  1153.     gtk_option_menu_set_menu (GTK_OPTION_MENU (option_menu), menu);
  1154.     gtk_widget_show (option_menu);
  1155.  
  1156.     cmap_preview = gtk_preview_new (GTK_PREVIEW_COLOR);
  1157.     gtk_preview_size (GTK_PREVIEW (cmap_preview), 32, 32);
  1158.  
  1159.     gtk_box_pack_end (GTK_BOX (hbox), cmap_preview, FALSE, FALSE, 0);
  1160.     gtk_widget_show (cmap_preview);
  1161.     set_cmap_preview ();
  1162.   }
  1163.  
  1164.   frame = gtk_frame_new (_("Camera"));
  1165.   gtk_box_pack_start (GTK_BOX (main_vbox), frame, FALSE, FALSE, 0);
  1166.   gtk_widget_show (frame);
  1167.  
  1168.   table = gtk_table_new (3, 3, FALSE);
  1169.   gtk_container_set_border_width (GTK_CONTAINER (table), 4);
  1170.   gtk_table_set_row_spacings (GTK_TABLE (table), 2);
  1171.   gtk_table_set_col_spacings (GTK_TABLE (table), 4);
  1172.   gtk_container_add (GTK_CONTAINER (frame), table);
  1173.   gtk_widget_show (table);
  1174.  
  1175.   adj = gimp_scale_entry_new (GTK_TABLE (table), 0, 0,
  1176.                   _("Zoom:"), SCALE_WIDTH, 0,
  1177.                   config.cp.zoom,
  1178.                   -4, 4, 0.5, 1, 2,
  1179.                   TRUE, 0, 0,
  1180.                   NULL, NULL);
  1181.   gtk_signal_connect (GTK_OBJECT (adj), "value_changed",
  1182.               GTK_SIGNAL_FUNC (gimp_double_adjustment_update),
  1183.               &config.cp.zoom);
  1184.   gtk_signal_connect (GTK_OBJECT (adj), "value_changed",
  1185.               GTK_SIGNAL_FUNC (set_flame_preview),
  1186.               NULL);
  1187.  
  1188.   adj = gimp_scale_entry_new (GTK_TABLE (table), 0, 1,
  1189.                   _("X:"), SCALE_WIDTH, 0,
  1190.                   config.cp.center[0],
  1191.                   -2, 2, 0.5, 1, 2,
  1192.                   TRUE, 0, 0,
  1193.                   NULL, NULL);
  1194.   gtk_signal_connect (GTK_OBJECT (adj), "value_changed",
  1195.               GTK_SIGNAL_FUNC (gimp_double_adjustment_update),
  1196.               &config.cp.center[0]);
  1197.   gtk_signal_connect (GTK_OBJECT (adj), "value_changed",
  1198.               GTK_SIGNAL_FUNC (set_flame_preview),
  1199.               NULL);
  1200.  
  1201.   adj = gimp_scale_entry_new (GTK_TABLE (table), 0, 2,
  1202.                   _("X:"), SCALE_WIDTH, 0,
  1203.                   config.cp.center[1],
  1204.                   -2, 2, 0.5, 1, 2,
  1205.                   TRUE, 0, 0,
  1206.                   NULL, NULL);
  1207.   gtk_signal_connect (GTK_OBJECT (adj), "value_changed",
  1208.               GTK_SIGNAL_FUNC (gimp_double_adjustment_update),
  1209.               &config.cp.center[1]);
  1210.   gtk_signal_connect (GTK_OBJECT (adj), "value_changed",
  1211.               GTK_SIGNAL_FUNC (set_flame_preview),
  1212.               NULL);
  1213.  
  1214.   flame_preview = gtk_preview_new (GTK_PREVIEW_COLOR);
  1215.   {
  1216.     gdouble aspect = config.cp.width / (double) config.cp.height;
  1217.  
  1218.     if (aspect > 1.0)
  1219.       {
  1220.     preview_width = PREVIEW_SIZE;
  1221.     preview_height = PREVIEW_SIZE / aspect;
  1222.       }
  1223.     else
  1224.       {
  1225.     preview_width = PREVIEW_SIZE * aspect;
  1226.     preview_height = PREVIEW_SIZE;
  1227.       }
  1228.   }
  1229.   gtk_preview_size (GTK_PREVIEW (flame_preview), preview_width, preview_height);
  1230.   gtk_container_add (GTK_CONTAINER (pframe), flame_preview);
  1231.   gtk_widget_show (flame_preview);
  1232.   set_flame_preview ();
  1233.  
  1234.   gtk_widget_show (dlg);
  1235.  
  1236.   gtk_main ();
  1237.   gdk_flush ();
  1238.  
  1239.   return run_flag;
  1240. }
  1241.