1 /******************************************************************************
2 
3   fractaltrace.c  -- This is a plug-in for GIMP 1.0
4 
5   Copyright (C) 1997  Hirotsuna Mizuno
6                       s1041150@u-aizu.ac.jp
7 
8   This program is free software: you can redistribute it and/or modify it
9   under the terms of the GNU General Public License as published by the Free
10   Software Foundation; either version 3 of the License, or (at your option)
11   any later version.
12 
13   This program is distributed in the hope that it will be useful, but WITHOUT
14   ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
15   FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
16   more details.
17 
18   You should have received a copy of the GNU General Public License along with
19   this program.  If not, see <https://www.gnu.org/licenses/>.
20 
21 ******************************************************************************/
22 
23 #define PLUG_IN_PROC     "plug-in-fractal-trace"
24 #define PLUG_IN_BINARY   "fractal-trace"
25 #define PLUG_IN_ROLE     "gimp-fractal-trace"
26 #define PLUG_IN_VERSION  "v0.4 test version (Dec. 25 1997)"
27 
28 /*****************************************************************************/
29 
30 #include "config.h"
31 
32 #include <libgimp/gimp.h>
33 #include <libgimp/gimpui.h>
34 
35 #include "libgimp/stdplugins-intl.h"
36 
37 /******************************************************************************/
38 
39 static void query  (void);
40 static void run    (const gchar      *name,
41                     gint              nparams,
42                     const GimpParam  *param,
43                     gint             *nreturn_vals,
44                     GimpParam       **return_vals);
45 
46 static void filter      (GimpDrawable *drawable);
47 
48 static void pixels_init (GimpDrawable *drawable);
49 static void pixels_free (void);
50 
51 static int  dialog_show         (void);
52 static void dialog_preview_draw (void);
53 
54 /******************************************************************************/
55 
56 const GimpPlugInInfo PLUG_IN_INFO =
57 {
58   NULL,  /* init_proc  */
59   NULL,  /* quit_proc  */
60   query, /* query_proc */
61   run    /* run_proc   */
62 };
63 
64 MAIN ()
65 
66 /******************************************************************************/
67 
68 enum
69 {
70   OUTSIDE_TYPE_WRAP,
71   OUTSIDE_TYPE_TRANSPARENT,
72   OUTSIDE_TYPE_BLACK,
73   OUTSIDE_TYPE_WHITE
74 };
75 
76 typedef struct
77 {
78   gdouble x1;
79   gdouble x2;
80   gdouble y1;
81   gdouble y2;
82   gint32  depth;
83   gint32  outside_type;
84 } parameter_t;
85 
86 static parameter_t parameters =
87 {
88   -1.0,
89   +0.5,
90   -1.0,
91   +1.0,
92   3,
93   OUTSIDE_TYPE_WRAP
94 };
95 
96 /******************************************************************************/
97 
98 static void
query(void)99 query (void)
100 {
101   static const GimpParamDef args[] =
102   {
103     { GIMP_PDB_INT32,    "run-mode",     "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
104     { GIMP_PDB_IMAGE,    "image",        "Input image (unused)"             },
105     { GIMP_PDB_DRAWABLE, "drawable",     "Input drawable"                   },
106     { GIMP_PDB_FLOAT,    "xmin",         "xmin fractal image delimiter"     },
107     { GIMP_PDB_FLOAT,    "xmax",         "xmax fractal image delimiter"     },
108     { GIMP_PDB_FLOAT,    "ymin",         "ymin fractal image delimiter"     },
109     { GIMP_PDB_FLOAT,    "ymax",         "ymax fractal image delimiter"     },
110     { GIMP_PDB_INT32,    "depth",        "Trace depth"                      },
111     { GIMP_PDB_INT32,    "outside-type", "Outside type "
112                                          "{ WRAP (0), TRANS (1), BLACK (2), WHITE (3) }" }
113   };
114 
115   gimp_install_procedure (PLUG_IN_PROC,
116                           N_("Transform image with the Mandelbrot Fractal"),
117                           "transform image with the Mandelbrot Fractal",
118                           "Hirotsuna Mizuno <s1041150@u-aizu.ac.jp>",
119                           "Copyright (C) 1997 Hirotsuna Mizuno",
120                           PLUG_IN_VERSION,
121                           N_("_Fractal Trace (legacy)..."),
122                           "RGB*, GRAY*",
123                           GIMP_PLUGIN,
124                           G_N_ELEMENTS (args), 0,
125                           args, NULL);
126 
127   gimp_plugin_menu_register (PLUG_IN_PROC, "<Image>/Filters/Map");
128 }
129 
130 /******************************************************************************/
131 
132 typedef struct
133 {
134   gint     x1;
135   gint     x2;
136   gint     y1;
137   gint     y2;
138   gint     width;
139   gint     height;
140   gdouble  center_x;
141   gdouble  center_y;
142 } selection_t;
143 
144 typedef struct
145 {
146   gint         width;
147   gint         height;
148   gint         bpp;
149   gint         alpha;
150 } image_t;
151 
152 static selection_t selection;
153 static image_t     image;
154 
155 /******************************************************************************/
156 
157 static void
run(const gchar * name,gint argc,const GimpParam * args,gint * retc,GimpParam ** rets)158 run (const gchar      *name,
159      gint              argc,
160      const GimpParam  *args,
161      gint             *retc,
162      GimpParam       **rets)
163 {
164   GimpDrawable      *drawable;
165   GimpRunMode        run_mode;
166   GimpPDBStatusType  status;
167   static GimpParam   returns[1];
168 
169   run_mode = args[0].data.d_int32;
170   status   = GIMP_PDB_SUCCESS;
171 
172   INIT_I18N ();
173 
174   drawable     = gimp_drawable_get (args[2].data.d_drawable);
175   image.width  = gimp_drawable_width( drawable->drawable_id);
176   image.height = gimp_drawable_height (drawable->drawable_id);
177   image.bpp    = gimp_drawable_bpp (drawable->drawable_id);
178   image.alpha  = gimp_drawable_has_alpha (drawable->drawable_id);
179 
180   if (! gimp_drawable_mask_intersect (drawable->drawable_id,
181                                       &selection.x1, &selection.y1,
182                                       &selection.width, &selection.height))
183     {
184       returns[0].type          = GIMP_PDB_STATUS;
185       returns[0].data.d_status = status;
186       *retc = 1;
187       *rets = returns;
188 
189       return;
190     }
191 
192   selection.x2       = selection.x1 + selection.width;
193   selection.y2       = selection.y1 + selection.height;
194   selection.center_x = selection.x1 + (gdouble) selection.width / 2.0;
195   selection.center_y = selection.y1 + (gdouble) selection.height / 2.0;
196 
197   pixels_init (drawable);
198 
199   if (!gimp_drawable_is_rgb(drawable->drawable_id) &&
200       !gimp_drawable_is_gray(drawable->drawable_id))
201     {
202       status = GIMP_PDB_EXECUTION_ERROR;
203     }
204 
205   switch (run_mode)
206     {
207     case GIMP_RUN_WITH_LAST_VALS:
208       gimp_get_data (PLUG_IN_PROC, &parameters);
209       break;
210 
211     case GIMP_RUN_INTERACTIVE:
212       gimp_get_data (PLUG_IN_PROC, &parameters);
213       if (!dialog_show ())
214         {
215           status = GIMP_PDB_EXECUTION_ERROR;
216           break;
217         }
218       gimp_set_data (PLUG_IN_PROC, &parameters, sizeof (parameter_t));
219       break;
220 
221     case GIMP_RUN_NONINTERACTIVE:
222       if (argc != 9)
223         {
224           status = GIMP_PDB_CALLING_ERROR;
225         }
226       else
227         {
228           parameters.x1           = args[3].data.d_float;
229           parameters.x2           = args[4].data.d_float;
230           parameters.y1           = args[5].data.d_float;
231           parameters.y2           = args[6].data.d_float;
232           parameters.depth        = args[7].data.d_int32;
233           parameters.outside_type = args[8].data.d_int32;
234         }
235       break;
236     }
237 
238   if (status == GIMP_PDB_SUCCESS)
239     {
240       gimp_tile_cache_ntiles(2 * (drawable->width / gimp_tile_width() + 1));
241       filter (drawable);
242       if (run_mode != GIMP_RUN_NONINTERACTIVE)
243         gimp_displays_flush();
244     }
245 
246   gimp_drawable_detach (drawable);
247 
248   pixels_free ();
249 
250   returns[0].type          = GIMP_PDB_STATUS;
251   returns[0].data.d_status = status;
252   *retc = 1;
253   *rets = returns;
254 }
255 
256 /******************************************************************************/
257 
258 static guchar    **spixels;
259 static guchar    **dpixels;
260 static GimpPixelRgn   sPR;
261 static GimpPixelRgn   dPR;
262 
263 typedef struct
264 {
265   guchar r;
266   guchar g;
267   guchar b;
268   guchar a;
269 } pixel_t;
270 
271 static void
pixels_init(GimpDrawable * drawable)272 pixels_init (GimpDrawable *drawable)
273 {
274   gint y;
275 
276   gimp_pixel_rgn_init (&sPR, drawable,
277                        0, 0, image.width, image.height, FALSE, FALSE);
278   gimp_pixel_rgn_init (&dPR, drawable,
279                        0, 0, image.width, image.height, TRUE, TRUE);
280 
281   spixels = g_new (guchar *, image.height);
282   dpixels = g_new (guchar *, image.height);
283 
284   for (y = 0; y < image.height; y++)
285     {
286       spixels[y] = g_new (guchar, image.width * image.bpp);
287       dpixels[y] = g_new (guchar, image.width * image.bpp);
288       gimp_pixel_rgn_get_row (&sPR, spixels[y], 0, y, image.width);
289     }
290 }
291 
292 static void
pixels_free(void)293 pixels_free (void)
294 {
295   gint y;
296 
297   for (y = 0; y < image.height; y++)
298     {
299       g_free (spixels[y]);
300       g_free (dpixels[y]);
301     }
302   g_free (spixels);
303   g_free (dpixels);
304 }
305 
306 static void
pixels_get(gint x,gint y,pixel_t * pixel)307 pixels_get (gint     x,
308             gint     y,
309             pixel_t *pixel)
310 {
311   if(x < 0) x = 0; else if (image.width  <= x) x = image.width  - 1;
312   if(y < 0) y = 0; else if (image.height <= y) y = image.height - 1;
313 
314   switch (image.bpp)
315     {
316     case 1: /* GRAY */
317       pixel->r = spixels[y][x*image.bpp];
318       pixel->g = spixels[y][x*image.bpp];
319       pixel->b = spixels[y][x*image.bpp];
320       pixel->a = 255;
321       break;
322     case 2: /* GRAY+A */
323       pixel->r = spixels[y][x*image.bpp];
324       pixel->g = spixels[y][x*image.bpp];
325       pixel->b = spixels[y][x*image.bpp];
326       pixel->a = spixels[y][x*image.bpp+1];
327       break;
328     case 3: /* RGB */
329       pixel->r = spixels[y][x*image.bpp];
330       pixel->g = spixels[y][x*image.bpp+1];
331       pixel->b = spixels[y][x*image.bpp+2];
332       pixel->a = 255;
333       break;
334     case 4: /* RGB+A */
335       pixel->r = spixels[y][x*image.bpp];
336       pixel->g = spixels[y][x*image.bpp+1];
337       pixel->b = spixels[y][x*image.bpp+2];
338       pixel->a = spixels[y][x*image.bpp+3];
339       break;
340     }
341 }
342 
343 static void
pixels_get_biliner(gdouble x,gdouble y,pixel_t * pixel)344 pixels_get_biliner (gdouble  x,
345                     gdouble  y,
346                     pixel_t *pixel)
347 {
348   pixel_t A, B, C, D;
349   gdouble a, b, c, d;
350   gint    x1, y1, x2, y2;
351   gdouble dx, dy;
352   gdouble alpha;
353 
354   x1 = (gint) floor (x);
355   x2 = x1 + 1;
356   y1 = (gint) floor (y);
357   y2 = y1 + 1;
358 
359   dx = x - (gdouble) x1;
360   dy = y - (gdouble) y1;
361   a  = (1.0 - dx) * (1.0 - dy);
362   b  = dx * (1.0 - dy);
363   c  = (1.0 - dx) * dy;
364   d  = dx * dy;
365 
366   pixels_get (x1, y1, &A);
367   pixels_get (x2, y1, &B);
368   pixels_get (x1, y2, &C);
369   pixels_get (x2, y2, &D);
370 
371   alpha = 1.0001*(a * (gdouble) A.a + b * (gdouble) B.a
372                  + c * (gdouble) C.a + d * (gdouble) D.a);
373   pixel->a = (guchar) alpha;
374 
375   if (pixel->a)
376     {
377       pixel->r = (guchar) ((a * (gdouble) A.r * A.a
378                             + b * (gdouble) B.r * B.a
379                             + c * (gdouble) C.r * C.a
380                             + d * (gdouble) D.r * D.a) / alpha);
381       pixel->g = (guchar) ((a * (gdouble) A.g * A.a
382                             + b * (gdouble) B.g * B.a
383                             + c * (gdouble) C.g * C.a
384                             + d * (gdouble) D.g * D.a) / alpha);
385       pixel->b = (guchar) ((a * (gdouble) A.b * A.a
386                             + b * (gdouble) B.b * B.a
387                             + c * (gdouble) C.b * C.a
388                             + d * (gdouble) D.b * D.a) / alpha);
389     }
390 }
391 
392 static void
pixels_set(gint x,gint y,pixel_t * pixel)393 pixels_set (gint     x,
394             gint     y,
395             pixel_t *pixel)
396 {
397   switch (image.bpp)
398     {
399     case 1: /* GRAY */
400       dpixels[y][x*image.bpp]   = pixel->r;
401       break;
402     case 2: /* GRAY+A */
403       dpixels[y][x*image.bpp]   = pixel->r;
404       dpixels[y][x*image.bpp+1] = pixel->a;
405       break;
406     case 3: /* RGB */
407       dpixels[y][x*image.bpp]   = pixel->r;
408       dpixels[y][x*image.bpp+1] = pixel->g;
409       dpixels[y][x*image.bpp+2] = pixel->b;
410       break;
411     case 4: /* RGB+A */
412       dpixels[y][x*image.bpp]   = pixel->r;
413       dpixels[y][x*image.bpp+1] = pixel->g;
414       dpixels[y][x*image.bpp+2] = pixel->b;
415       dpixels[y][x*image.bpp+3] = pixel->a;
416       break;
417     }
418 }
419 
420 static void
pixels_store(void)421 pixels_store (void)
422 {
423   gint y;
424 
425   for (y = selection.y1; y < selection.y2; y++)
426     {
427       gimp_pixel_rgn_set_row (&dPR, dpixels[y], 0, y, image.width);
428     }
429 }
430 
431 /******************************************************************************/
432 
433 static void
mandelbrot(gdouble x,gdouble y,gdouble * u,gdouble * v)434 mandelbrot (gdouble  x,
435             gdouble  y,
436             gdouble *u,
437             gdouble *v)
438 {
439   gint    iter = 0;
440   gdouble xx   = x;
441   gdouble yy   = y;
442   gdouble x2   = xx * xx;
443   gdouble y2   = yy * yy;
444   gdouble tmp;
445 
446   while (iter < parameters.depth)
447     {
448       tmp = x2 - y2 + x;
449       yy  = 2 * xx * yy + y;
450       xx  = tmp;
451       x2  = xx * xx;
452       y2  = yy * yy;
453       iter++;
454     }
455   *u = xx;
456   *v = yy;
457 }
458 
459 /******************************************************************************/
460 
461 static void
filter(GimpDrawable * drawable)462 filter (GimpDrawable *drawable)
463 {
464   gint    x, y;
465   pixel_t pixel;
466   gdouble scale_x, scale_y;
467   gdouble cx, cy;
468   gdouble px, py;
469   gint h_percent;
470 
471   gimp_progress_init (_("Fractal Trace"));
472 
473   if (selection.width == 0 || selection.height == 0)
474     return;
475 
476   h_percent = selection.height / 100;
477 
478   scale_x = (parameters.x2 - parameters.x1) / selection.width;
479   scale_y = (parameters.y2 - parameters.y1) / selection.height;
480 
481   for (y = selection.y1; y < selection.y2; y++)
482     {
483       cy = parameters.y1 + (y - selection.y1) * scale_y;
484       for (x = selection.x1; x < selection.x2; x++)
485         {
486           cx = parameters.x1 + (x - selection.x1) * scale_x;
487           mandelbrot (cx, cy, &px, &py);
488           px = (px - parameters.x1) / scale_x + selection.x1;
489           py = (py - parameters.y1) / scale_y + selection.y1;
490           if (0 <= px && px < image.width && 0 <= py && py < image.height)
491             {
492               pixels_get_biliner (px, py, &pixel);
493             }
494           else
495             {
496               switch (parameters.outside_type)
497                 {
498                 case OUTSIDE_TYPE_WRAP:
499                   px = fmod (px, image.width);
500                   py = fmod (py, image.height);
501                   if( px < 0.0) px += image.width;
502                   if (py < 0.0) py += image.height;
503                   pixels_get_biliner (px, py, &pixel);
504                   break;
505                 case OUTSIDE_TYPE_TRANSPARENT:
506                   pixel.r = pixel.g = pixel.b = 0;
507                   pixel.a = 0;
508                   break;
509                 case OUTSIDE_TYPE_BLACK:
510                   pixel.r = pixel.g = pixel.b = 0;
511                   pixel.a = 255;
512                   break;
513                 case OUTSIDE_TYPE_WHITE:
514                   pixel.r = pixel.g = pixel.b = 255;
515                   pixel.a = 255;
516                   break;
517                 }
518             }
519           pixels_set (x, y, &pixel);
520         }
521 
522       if (h_percent == 0 || ((y - selection.y1) % h_percent) == 0)
523         gimp_progress_update ((gdouble) (y-selection.y1) / selection.height);
524     }
525 
526   gimp_progress_update (1.0);
527 
528   pixels_store ();
529 
530   gimp_drawable_flush (drawable);
531   gimp_drawable_merge_shadow (drawable->drawable_id, TRUE);
532   gimp_drawable_update (drawable->drawable_id,
533                         selection.x1, selection.y1,
534                         selection.width, selection.height);
535 }
536 
537 /******************************************************************************/
538 
539 #define PREVIEW_SIZE 200
540 
541 typedef struct
542 {
543   GtkWidget  *preview;
544   guchar     *pixels;
545   gdouble     scale;
546   gint        width;
547   gint        height;
548   gint        bpp;
549 } preview_t;
550 
551 static preview_t preview;
552 
553 static void
dialog_preview_setpixel(gint x,gint y,pixel_t * pixel)554 dialog_preview_setpixel (gint     x,
555                          gint     y,
556                          pixel_t *pixel)
557 {
558   preview.pixels[(y * preview.width + x) * 4 + 0] = pixel->r;
559   preview.pixels[(y * preview.width + x) * 4 + 1] = pixel->g;
560   preview.pixels[(y * preview.width + x) * 4 + 2] = pixel->b;
561   preview.pixels[(y * preview.width + x) * 4 + 3] = pixel->a;
562 }
563 
564 static void
dialog_preview_init(void)565 dialog_preview_init (void)
566 {
567   pixel_t  pixel;
568   gint     x, y;
569   gdouble  cx, cy;
570 
571   if (image.width < image.height)
572     preview.scale = (gdouble)selection.height / (gdouble)PREVIEW_SIZE;
573   else
574     preview.scale = (gdouble)selection.width / (gdouble)PREVIEW_SIZE;
575   preview.width  = (gdouble)selection.width / preview.scale;
576   preview.height = (gdouble)selection.height / preview.scale;
577 
578   preview.preview = gimp_preview_area_new ();
579   gtk_widget_set_size_request (preview.preview,
580                                preview.width, preview.height);
581 
582   preview.pixels = g_new (guchar, preview.height * preview.width * 4);
583 
584   for (y = 0; y < preview.height; y++)
585     {
586       cy = selection.y1 + (gdouble)y * preview.scale;
587       for (x = 0; x < preview.width; x++)
588         {
589           cx = selection.x1 + (gdouble)x * preview.scale;
590           pixels_get_biliner (cx, cy, &pixel);
591           dialog_preview_setpixel (x, y, &pixel);
592         }
593     }
594   gimp_preview_area_draw (GIMP_PREVIEW_AREA (preview.preview),
595                           0, 0, preview.width, preview.height,
596                           GIMP_RGBA_IMAGE,
597                           preview.pixels,
598                           preview.width *4);
599 }
600 
601 static void
dialog_preview_draw(void)602 dialog_preview_draw (void)
603 {
604   gint    x, y;
605   pixel_t pixel;
606   gdouble scale_x, scale_y;
607   gdouble cx, cy;
608   gdouble px, py;
609 
610   scale_x = (parameters.x2 - parameters.x1) / preview.width;
611   scale_y = (parameters.y2 - parameters.y1) / preview.height;
612 
613   for (y = 0; y < preview.height; y++)
614     {
615       cy = parameters.y1 + y * scale_y;
616       for (x = 0; x < preview.width; x++)
617         {
618           cx = parameters.x1 + x * scale_x;
619           mandelbrot(cx, cy, &px, &py);
620           px = (px - parameters.x1) / scale_x * preview.scale + selection.x1;
621           py = (py - parameters.y1) / scale_y * preview.scale + selection.y1;
622           if (0 <= px && px < image.width && 0 <= py && py < image.height)
623             {
624               pixels_get_biliner (px, py, &pixel);
625             }
626           else
627             {
628               switch (parameters.outside_type)
629                 {
630                 case OUTSIDE_TYPE_WRAP:
631                   px = fmod (px, image.width);
632                   py = fmod (py, image.height);
633                   if (px < 0.0) px += image.width;
634                   if (py < 0.0) py += image.height;
635                   pixels_get_biliner (px, py, &pixel);
636                   break;
637                 case OUTSIDE_TYPE_TRANSPARENT:
638                   pixel.r = pixel.g = pixel.b = 0;
639                   pixel.a = 0;
640                   break;
641                 case OUTSIDE_TYPE_BLACK:
642                   pixel.r = pixel.g = pixel.b = 0;
643                   pixel.a = 255;
644                   break;
645                 case OUTSIDE_TYPE_WHITE:
646                   pixel.r = pixel.g = pixel.b = 255;
647                   pixel.a = 255;
648                   break;
649                 }
650             }
651           dialog_preview_setpixel (x, y, &pixel);
652         }
653     }
654   gimp_preview_area_draw (GIMP_PREVIEW_AREA (preview.preview),
655                           0, 0, preview.width, preview.height,
656                           GIMP_RGBA_IMAGE,
657                           preview.pixels,
658                           preview.width *4);
659 }
660 
661 /******************************************************************************/
662 
663 static void
dialog_int_adjustment_update(GtkAdjustment * adjustment,gpointer data)664 dialog_int_adjustment_update (GtkAdjustment *adjustment,
665                               gpointer       data)
666 {
667   gimp_int_adjustment_update (adjustment, data);
668 
669   dialog_preview_draw ();
670 }
671 
672 static void
dialog_double_adjustment_update(GtkAdjustment * adjustment,gpointer data)673 dialog_double_adjustment_update (GtkAdjustment *adjustment,
674                                  gpointer       data)
675 {
676   gimp_double_adjustment_update (adjustment, data);
677 
678   dialog_preview_draw ();
679 }
680 
681 static void
dialog_outside_type_callback(GtkWidget * widget,gpointer * data)682 dialog_outside_type_callback (GtkWidget *widget,
683                               gpointer  *data)
684 {
685   gimp_radio_button_update (widget, data);
686 
687   if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget)))
688     dialog_preview_draw ();
689 }
690 
691 /******************************************************************************/
692 
693 static gboolean
dialog_show(void)694 dialog_show (void)
695 {
696   GtkWidget *dialog;
697   GtkWidget *mainbox;
698   GtkWidget *hbox;
699   GtkWidget *table;
700   GtkWidget *frame;
701   GtkWidget *abox;
702   GtkObject *adj;
703   gboolean   run;
704 
705   gimp_ui_init (PLUG_IN_BINARY, TRUE);
706 
707   dialog = gimp_dialog_new (_("Fractal Trace"), PLUG_IN_ROLE,
708                             NULL, 0,
709                             gimp_standard_help_func, PLUG_IN_PROC,
710 
711                             _("_Cancel"), GTK_RESPONSE_CANCEL,
712                             _("_OK"),     GTK_RESPONSE_OK,
713 
714                             NULL);
715 
716   gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
717                                            GTK_RESPONSE_OK,
718                                            GTK_RESPONSE_CANCEL,
719                                            -1);
720 
721   gimp_window_set_transient (GTK_WINDOW (dialog));
722 
723   mainbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
724   gtk_container_set_border_width (GTK_CONTAINER (mainbox), 12);
725   gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))),
726                       mainbox, TRUE, TRUE, 0);
727   gtk_widget_show (mainbox);
728 
729   hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
730   gtk_box_pack_start (GTK_BOX (mainbox), hbox, FALSE, FALSE, 0);
731   gtk_widget_show (hbox);
732 
733   /*  Preview  */
734   abox = gtk_alignment_new (0.0, 0.0, 0.0, 0.0);
735   gtk_box_pack_start (GTK_BOX (hbox), abox, FALSE, FALSE, 0);
736   gtk_widget_show (abox);
737 
738   frame = gtk_frame_new (NULL);
739   gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
740   gtk_container_add (GTK_CONTAINER (abox), frame);
741   gtk_widget_show (frame);
742 
743   dialog_preview_init ();
744   gtk_container_add (GTK_CONTAINER (frame), preview.preview);
745   gtk_widget_show (preview.preview);
746 
747   /*  Settings  */
748   frame = gimp_int_radio_group_new (TRUE, _("Outside Type"),
749                                     G_CALLBACK (dialog_outside_type_callback),
750                                     &parameters.outside_type,
751                                     parameters.outside_type,
752 
753                                     _("_Wrap"),
754                                     OUTSIDE_TYPE_WRAP, NULL,
755                                     _("_Transparent"),
756                                     OUTSIDE_TYPE_TRANSPARENT, NULL,
757                                     _("_Black"),
758                                     OUTSIDE_TYPE_BLACK, NULL,
759                                     _("_White"),
760                                     OUTSIDE_TYPE_WHITE, NULL,
761 
762                                  NULL);
763   gtk_box_pack_start (GTK_BOX (hbox), frame, FALSE, FALSE, 0);
764   gtk_widget_show (frame);
765 
766   frame = gimp_frame_new (_("Mandelbrot Parameters"));
767   gtk_box_pack_start (GTK_BOX (mainbox), frame, FALSE, FALSE, 0);
768   gtk_widget_show (frame);
769 
770   table = gtk_table_new (5, 3, FALSE);
771   gtk_table_set_row_spacings (GTK_TABLE (table), 6);
772   gtk_table_set_col_spacings (GTK_TABLE (table), 6);
773   gtk_container_add (GTK_CONTAINER (frame), table);
774   gtk_widget_show (table);
775 
776   adj = gimp_scale_entry_new (GTK_TABLE (table), 0, 0,
777                               _("X_1:"), 0, 6,
778                               parameters.x1, -50, 50, 0.1, 0.5, 2,
779                               TRUE, 0, 0,
780                               NULL, NULL);
781   g_signal_connect (adj, "value-changed",
782                     G_CALLBACK (dialog_double_adjustment_update),
783                     &parameters.x1);
784 
785   adj = gimp_scale_entry_new (GTK_TABLE (table), 0, 1,
786                               _("X_2:"), 0, 6,
787                               parameters.x2, -50, 50, 0.1, 0.5, 2,
788                               TRUE, 0, 0,
789                               NULL, NULL);
790   g_signal_connect (adj, "value-changed",
791                     G_CALLBACK (dialog_double_adjustment_update),
792                     &parameters.x2);
793 
794   adj = gimp_scale_entry_new (GTK_TABLE (table), 0, 2,
795                               _("Y_1:"), 0, 6,
796                               parameters.y1, -50, 50, 0.1, 0.5, 2,
797                               TRUE, 0, 0,
798                               NULL, NULL);
799   g_signal_connect (adj, "value-changed",
800                     G_CALLBACK (dialog_double_adjustment_update),
801                     &parameters.y1);
802 
803   adj = gimp_scale_entry_new (GTK_TABLE (table), 0, 3,
804                               _("Y_2:"), 0, 6,
805                               parameters.y2, -50, 50, 0.1, 0.5, 2,
806                               TRUE, 0, 0,
807                               NULL, NULL);
808   g_signal_connect (adj, "value-changed",
809                     G_CALLBACK (dialog_double_adjustment_update),
810                     &parameters.y2);
811 
812   adj = gimp_scale_entry_new (GTK_TABLE (table), 0, 4,
813                               _("_Depth:"), 0, 6,
814                               parameters.depth, 1, 50, 1, 5, 0,
815                               TRUE, 0, 0,
816                               NULL, NULL);
817   g_signal_connect (adj, "value-changed",
818                     G_CALLBACK (dialog_int_adjustment_update),
819                     &parameters.depth);
820 
821   gtk_widget_show (dialog);
822   dialog_preview_draw ();
823 
824   run = (gimp_dialog_run (GIMP_DIALOG (dialog)) == GTK_RESPONSE_OK);
825 
826   gtk_widget_destroy (dialog);
827 
828   return run;
829 }
830