home *** CD-ROM | disk | FTP | other *** search
/ PC Pro 2002 April / pcpro0402.iso / essentials / graphics / Gimp / gimp-src-20001226.exe / src / gimp / plug-ins / gflare / gflare.c < prev    next >
Encoding:
C/C++ Source or Header  |  2000-12-10  |  142.6 KB  |  5,351 lines

  1. /* The GIMP -- an image manipulation program
  2.  * Copyright (C) 1995 Spencer Kimball and Peter Mattis
  3.  *
  4.  * GFlare plug-in -- lense flare effect by using custom gradients
  5.  * Copyright (C) 1997 Eiichi Takamori <taka@ma1.sekyou.ne.jp>
  6.  *
  7.  * This program is free software; you can redistribute it and/or modify
  8.  * it under the terms of the GNU General Public License as published by
  9.  * the Free Software Foundation; either version 2 of the License, or
  10.  * (at your option) any later version.
  11.  *
  12.  * This program is distributed in the hope that it will be useful,
  13.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.     See the
  15.  * GNU General Public License for more details.
  16.  *
  17.  * You should have received a copy of the GNU General Public License
  18.  * along with this program; if not, write to the Free Software
  19.  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  20.  *
  21.  *
  22.  * A fair proportion of this code was taken from GIMP & Script-fu
  23.  * copyrighted by Spencer Kimball and Peter Mattis, and from Gradient
  24.  * Editor copyrighted by Federico Mena Quintero. (See copyright notice
  25.  * below) Thanks for senior GIMP hackers!!
  26.  *
  27.  * The GIMP -- an image manipulation program
  28.  * Copyright (C) 1995 Spencer Kimball and Peter Mattis
  29.  *
  30.  * Gradient editor module copyight (C) 1996-1997 Federico Mena Quintero
  31.  * federico@nuclecu.unam.mx
  32.  */
  33.  
  34. /*
  35.   version 0.27
  36.   Changed so that it works with GIMP 1.1.x
  37.   Default problem solved
  38.   martweb@gmx.net
  39.  */
  40.  
  41. #ifdef RCSID
  42. static char rcsid[] = "$Id: gflare.c,v 1.38 2000/12/03 18:35:58 mitch Exp $";
  43. #endif
  44.  
  45. #ifdef HAVE_CONFIG_H
  46. #include "config.h"
  47. #endif
  48.  
  49. #include <stdio.h>
  50. #include <stdlib.h>
  51. #include <string.h>
  52. #include <ctype.h>
  53. #include <time.h>
  54. #include <sys/types.h>
  55. #include <sys/stat.h>
  56. #ifdef HAVE_UNISTD_H
  57. #include <unistd.h>
  58. #endif
  59. #ifdef HAVE_DIRENT_H
  60. #include <dirent.h>
  61. #endif
  62.  
  63. #ifdef G_OS_WIN32
  64. #include <io.h>
  65. #ifndef S_ISREG
  66. #define S_ISREG(m) ((m) & _S_IFREG)
  67. #endif
  68. #endif
  69.  
  70. #include <gtk/gtk.h>
  71.  
  72. #include <libgimp/gimp.h>
  73. #include <libgimp/gimpui.h>
  74.  
  75. #include "libgimp/stdplugins-intl.h"
  76.  
  77. #include "asupsample.h"
  78. #include "gtkmultioptionmenu.h"
  79.  
  80. /* #define DEBUG */
  81.  
  82. #ifdef DEBUG
  83. #define DEBUG_PRINT(X) g_print X
  84. #else
  85. #define DEBUG_PRINT(X)
  86. #endif
  87.  
  88. #define LUMINOSITY(PIX) (INTENSITY (PIX[0], PIX[1], PIX[2]))
  89. #define OFFSETOF(t,f)    ((int) ((char*) &((t*) 0)->f))
  90.  
  91. #define GRADIENT_NAME_MAX   256
  92. #define GRADIENT_RESOLUTION 360
  93.  
  94. #define GFLARE_NAME_MAX     256
  95. #define GFLARE_FILE_HEADER  "GIMP GFlare 0.25\n"
  96. #define SFLARE_NUM         30
  97.  
  98. #define DLG_PREVIEW_WIDTH  256
  99. #define DLG_PREVIEW_HEIGHT 256
  100. #define DLG_PREVIEW_MASK   GDK_EXPOSURE_MASK | \
  101.                GDK_BUTTON_PRESS_MASK
  102. #define DLG_LISTBOX_WIDTH  80
  103. #define DLG_LISTBOX_HEIGHT 40
  104.  
  105. #define ED_PREVIEW_WIDTH  256
  106. #define ED_PREVIEW_HEIGHT 256
  107.  
  108. #define GM_PREVIEW_WIDTH  80
  109. #define GM_PREVIEW_HEIGHT 16
  110. #define GM_MENU_MAX       20
  111.  
  112. #define SCALE_WIDTH 100
  113. #define ENTRY_WIDTH  40
  114.  
  115. #ifndef OPAQUE
  116. #define OPAQUE       255
  117. #endif
  118. #define GRAY50       128
  119.  
  120. /* drawable coord to preview coord */
  121. #define DX2PX(DX)  ((double) PREVIEW_WIDTH * ((DX) - dinfo->win.x0) / (dinfo->win.x1 - dinfo->win.x0))
  122. #define DY2PY(DY)  ((double) PREVIEW_HEIGHT * ((DY) - dinfo->win.y0) / (dinfo->win.y1 - dinfo->win.y0))
  123. #define GRADIENT_CACHE_SIZE 32
  124.  
  125. #define CALC_GLOW   0x01
  126. #define CALC_RAYS   0x02
  127. #define CALC_SFLARE 0x04
  128.  
  129. typedef struct _Preview Preview;
  130.  
  131. typedef gchar    GradientName[GRADIENT_NAME_MAX];
  132.  
  133. typedef enum
  134. {
  135.   GF_NORMAL = 0,
  136.   GF_ADDITION,
  137.   GF_OVERLAY,
  138.   GF_SCREEN,
  139.   GF_NUM_MODES
  140. } GFlareMode;
  141.  
  142. typedef enum
  143. {
  144.   GF_CIRCLE = 0,
  145.   GF_POLYGON,
  146.   GF_NUM_SHAPES
  147. } GFlareShape;
  148.  
  149. typedef struct
  150. {
  151.   gchar        *name;
  152.   gchar        *filename;
  153.   gdouble       glow_opacity;
  154.   GFlareMode    glow_mode;
  155.   gdouble       rays_opacity;
  156.   GFlareMode    rays_mode;
  157.   gdouble       sflare_opacity;
  158.   GFlareMode    sflare_mode;
  159.   GradientName  glow_radial;
  160.   GradientName  glow_angular;
  161.   GradientName  glow_angular_size;
  162.   gdouble       glow_size;
  163.   gdouble       glow_rotation;
  164.   gdouble       glow_hue;
  165.   GradientName  rays_radial;
  166.   GradientName  rays_angular;
  167.   GradientName  rays_angular_size;
  168.   gdouble       rays_size;
  169.   gdouble       rays_rotation;
  170.   gdouble       rays_hue;
  171.   gint          rays_nspikes;
  172.   gdouble       rays_thickness;
  173.   GradientName  sflare_radial;
  174.   GradientName  sflare_sizefac;
  175.   GradientName  sflare_probability;
  176.   gdouble       sflare_size;
  177.   gdouble       sflare_rotation;
  178.   gdouble       sflare_hue;
  179.   GFlareShape   sflare_shape;
  180.   gint          sflare_nverts;
  181.   gint          sflare_seed;
  182.   gint          sflare_seed_time;
  183. } GFlare;
  184.  
  185. typedef struct
  186. {
  187.   FILE *fp;
  188.   gint  error;
  189. } GFlareFile;
  190.  
  191.  
  192. typedef enum
  193. {
  194.   PAGE_SETTINGS,
  195.   PAGE_SELECTOR,
  196.   PAGE_GENERAL,
  197.   PAGE_GLOW,
  198.   PAGE_RAYS,
  199.   PAGE_SFLARE
  200. } PageNum;
  201.  
  202.  
  203. typedef struct
  204. {
  205.   gint       init;
  206.   GFlare    *gflare;
  207.   GtkWidget *shell;
  208.   Preview   *preview;
  209.   struct
  210.   {
  211.     gdouble x0, y0, x1, y1;
  212.   } pwin;
  213.   gint       update_preview;
  214.   GtkWidget *notebook;
  215.   GtkWidget *sizeentry;
  216.   GtkWidget *asupsample_frame;
  217.   GtkWidget *selector_list;
  218.   gint       init_params_done;
  219. } GFlareDialog;
  220.  
  221. typedef void (* GFlareEditorCallback) (gint updated, gpointer data);
  222.  
  223. typedef struct
  224. {
  225.   gint                  init;
  226.   gint                  run;
  227.   GFlareEditorCallback  callback;
  228.   gpointer              calldata;
  229.   GFlare               *target_gflare;
  230.   GFlare               *gflare;
  231.   GtkWidget            *shell;
  232.   Preview              *preview;
  233.   GtkWidget            *notebook;
  234.   PageNum               cur_page;
  235.   GtkWidget            *polygon_entry;
  236.   GtkWidget            *polygon_toggle;
  237.   gint                  init_params_done;
  238. } GFlareEditor;
  239.  
  240. typedef struct
  241. {
  242.   gdouble x0;
  243.   gdouble y0;
  244.   gdouble x1;
  245.   gdouble y1;
  246. } CalcBounds;
  247.  
  248. typedef struct
  249. {
  250.   gint     init;
  251.   gint     type;
  252.   GFlare  *gflare;
  253.   gdouble  xcenter;
  254.   gdouble  ycenter;
  255.   gdouble  radius;
  256.   gdouble  rotation;
  257.   gdouble  hue;
  258.   gdouble  vangle;
  259.   gdouble  vlength;
  260.  
  261.   gint        glow_opacity;
  262.   CalcBounds  glow_bounds;
  263.   guchar     *glow_radial;
  264.   guchar     *glow_angular;
  265.   guchar     *glow_angular_size;
  266.   gdouble     glow_radius;
  267.   gdouble     glow_rotation;
  268.  
  269.   gint        rays_opacity;
  270.   CalcBounds  rays_bounds;
  271.   guchar     *rays_radial;
  272.   guchar     *rays_angular;
  273.   guchar     *rays_angular_size;
  274.   gdouble     rays_radius;
  275.   gdouble     rays_rotation;
  276.   gdouble     rays_spike_mod;
  277.   gdouble     rays_thinness;
  278.  
  279.   gint         sflare_opacity;
  280.   GList       *sflare_list;
  281.   guchar      *sflare_radial;
  282.   guchar      *sflare_sizefac;
  283.   guchar      *sflare_probability;
  284.   gdouble      sflare_radius;
  285.   gdouble      sflare_rotation;
  286.   GFlareShape  sflare_shape;
  287.   gdouble      sflare_angle;
  288.   gdouble      sflare_factor;
  289. } CalcParams;
  290.  
  291. /*
  292.  * What's the difference between (structure) CalcParams and GFlare ?
  293.  * well, radius and lengths are actual length for CalcParams where
  294.  * they are typically 0 to 100 for GFlares, and angles are G_PI based
  295.  * (radian) for CalcParams where they are degree for GFlares. et cetra.
  296.  * This is because convienience for dialog processing and for calculating.
  297.  * these conversion is taken place in calc init routines. see below.
  298.  */
  299.  
  300. typedef struct
  301. {
  302.   gdouble    xcenter;
  303.   gdouble    ycenter;
  304.   gdouble    radius;
  305.   CalcBounds bounds;
  306. } CalcSFlare;
  307.  
  308. typedef struct
  309. {
  310.   gint is_color;
  311.   gint has_alpha;
  312.   gint x1, y1, x2, y2;        /* mask bounds */
  313.   gint tile_width, tile_height;
  314.   /* these values don't belong to drawable, though. */
  315. } DrawableInfo;
  316.  
  317. typedef struct
  318. {
  319.   GimpTile *tile;
  320.   gint      col;
  321.   gint      row;
  322.   gint      shadow;
  323.   gint      dirty;
  324. } TileKeeper;
  325.  
  326. typedef struct _GradientMenu GradientMenu;
  327. typedef void (* GradientMenuCallback) (gchar    *gradient_name,
  328.                        gpointer  data);
  329. struct _GradientMenu
  330. {
  331.   GtkWidget            *preview;
  332.   GtkWidget            *option_menu;
  333.   GradientMenuCallback  callback;
  334.   gpointer              callback_data;
  335.   GradientName          gradient_name;
  336. };
  337.  
  338. typedef gint (* PreviewInitFunc)   (Preview *preview, gpointer data);
  339. typedef void (* PreviewRenderFunc) (Preview *preview, guchar *buffer,
  340.                     gint y, gpointer data);
  341. typedef void (* PreviewDeinitFunc) (Preview *preview, gpointer data);
  342.  
  343. struct _Preview
  344. {
  345.   GtkWidget         *widget;
  346.   gint               width;
  347.   gint               height;
  348.   PreviewInitFunc    init_func;
  349.   gpointer           init_data;
  350.   PreviewRenderFunc  render_func;
  351.   gpointer           render_data;
  352.   PreviewDeinitFunc  deinit_func;
  353.   gpointer           deinit_data;
  354.   guint              timeout_tag;
  355.   guint              idle_tag;
  356.   gint               init_done;
  357.   gint               current_y;
  358.   gint               drawn_y;
  359.   guchar            *buffer;
  360. };
  361.  
  362. typedef struct
  363. {
  364.   gint               tag;
  365.   gint               got_gradients;
  366.   gint               current_y;
  367.   gint               drawn_y;
  368.   PreviewRenderFunc  render_func;
  369.   guchar            *buffer;
  370. } PreviewIdle;
  371.  
  372. typedef struct _GradientCacheItem  GradientCacheItem;
  373.  
  374. struct _GradientCacheItem
  375. {
  376.   GradientCacheItem *next;
  377.   GradientCacheItem *prev;
  378.   GradientName       name;
  379.   guchar             values[4 * GRADIENT_RESOLUTION];
  380. };
  381.  
  382. typedef struct
  383. {
  384.   gint     xcenter;
  385.   gint     ycenter;
  386.   gdouble  radius;
  387.   gdouble  rotation;
  388.   gdouble  hue;
  389.   gdouble  vangle;
  390.   gdouble  vlength;
  391.   gint     use_asupsample;
  392.   gint     asupsample_max_depth;
  393.   gdouble  asupsample_threshold;
  394.   gchar    gflare_name[GFLARE_NAME_MAX];
  395. } PluginValues;
  396.  
  397. typedef struct
  398. {
  399.   gint  run;
  400. } PluginInterface;
  401.  
  402. typedef void (*QueryFunc) (GtkWidget *, gpointer, gpointer);
  403.  
  404. /***
  405.  ***  Global Functions Prototypes
  406.  **/
  407.  
  408. static void    plugin_query (void);
  409. static void    plugin_run   (gchar      *name,
  410.                  gint        nparams,
  411.                  GimpParam  *param,
  412.                  gint       *nreturn_vals,
  413.                  GimpParam **return_vals);
  414.  
  415. static void     plug_in_parse_gflare_path (void);
  416.  
  417. static GFlare * gflare_new_with_default (gchar  *new_name);
  418. static GFlare * gflare_dup              (GFlare *src,
  419.                                          gchar  *new_name);
  420. static void     gflare_copy             (GFlare *dest,
  421.                      GFlare *src);
  422. static GFlare * gflare_load             (gchar  *filename,
  423.                      gchar  *name);
  424. static void     gflare_save             (GFlare *gflare);
  425. static void     gflare_name_copy        (gchar  *dest,
  426.                      gchar  *src);
  427.  
  428. static gint     gflares_list_insert     (GFlare *gflare);
  429. static GFlare * gflares_list_lookup     (gchar *name);
  430. static gint     gflares_list_index      (GFlare *gflare);
  431. static gint     gflares_list_remove     (GFlare *gflare);
  432. static void     gflares_list_load_all   (void);
  433. static void     gflares_list_free_all   (void);
  434.  
  435. static void     calc_init_params   (GFlare  *gflare,
  436.                     gint     calc_type,
  437.                     gdouble  xcenter,
  438.                     gdouble  ycenter,
  439.                     gdouble  radius,
  440.                     gdouble  rotation,
  441.                     gdouble  hue,
  442.                     gdouble  vangle,
  443.                     gdouble  vlength);
  444. static gint     calc_init_progress (void);
  445. static void     calc_deinit        (void);
  446. static void     calc_glow_pix      (guchar  *dest_pix,
  447.                     gdouble  x,
  448.                     gdouble  y);
  449. static void     calc_rays_pix      (guchar  *dest_pix,
  450.                     gdouble  x,
  451.                     gdouble  y);
  452. static void     calc_sflare_pix    (guchar  *dest_pix,
  453.                     gdouble  x,
  454.                     gdouble  y,
  455.                     guchar  *src_pix);
  456. static void     calc_gflare_pix    (guchar  *dest_pix,
  457.                     gdouble  x,
  458.                     gdouble  y,
  459.                     guchar  *src_pix);
  460.  
  461. static gint        dlg_run                 (void);
  462. static void        dlg_preview_calc_window (void);
  463. static void        ed_preview_calc_window  (void);
  464. static GtkWidget * ed_mode_menu_new        (GFlareMode *mode_var);
  465.  
  466. static Preview   * preview_new          (gint               width,
  467.                      gint               height,
  468.                      PreviewInitFunc    init_func,
  469.                      gpointer           init_data,
  470.                      PreviewRenderFunc  render_func,
  471.                      gpointer           render_data,
  472.                      PreviewDeinitFunc  deinit_func,
  473.                      gpointer           deinit_data);
  474. static void        preview_free         (Preview           *preview);
  475. static void        preview_render_start (Preview           *preview);
  476. static void        preview_render_end   (Preview           *preview);
  477. static void        preview_rgba_to_rgb  (guchar            *dest,
  478.                      gint               x,
  479.                      gint               y,
  480.                      guchar            *src);
  481.  
  482. static void             gradient_menu_init    (void);
  483. static void             gradient_menu_rescan  (void);
  484. static GradientMenu   * gradient_menu_new     (GradientMenuCallback callback,
  485.                            gpointer  callback_data,
  486.                            gchar    *default_gradient_name);
  487. /*
  488. static void             gradient_menu_destroy (GradientMenu *gm);
  489. */
  490. static void             gradient_name_copy    (gchar  *dest,
  491.                            gchar  *src);
  492. static void             gradient_name_encode  (guchar *dest,
  493.                            guchar *src);
  494. static void             gradient_name_decode  (guchar *dest,
  495.                            guchar *src);
  496. static void             gradient_init         (void);
  497. static void             gradient_free         (void);
  498. static gchar         ** gradient_get_list     (gint   *num_gradients);
  499. static void             gradient_get_values   (gchar  *gradient_name,
  500.                            guchar *values,
  501.                            gint    nvalues);
  502. static void             gradient_cache_flush  (void);
  503.  
  504. /* *** INSERT-FILE-END *** */
  505.  
  506. /**
  507. ***    Variables
  508. **/
  509.  
  510. GimpPlugInInfo PLUG_IN_INFO =
  511. {
  512.   NULL,            /* init_proc  */
  513.   NULL,            /* quit_proc  */
  514.   plugin_query,    /* query_proc */
  515.   plugin_run,   /* run_proc   */
  516. };
  517.  
  518. PluginValues pvals =
  519. {
  520.   128,        /* xcenter */
  521.   128,        /* ycenter */
  522.   100.0,    /* radius */
  523.   0.0,        /* rotation */
  524.   0.0,        /* hue */
  525.   60.0,        /* vangle */
  526.   400.0,    /* vlength */
  527.   FALSE,    /* use_asupsample */
  528.   3,        /* asupsample_max_depth */
  529.   0.2,        /* asupsample_threshold */
  530.   "Default"    /* gflare_name */
  531. };
  532.  
  533. PluginInterface pint =
  534. {
  535.   FALSE                /* run */
  536. };
  537.  
  538. GFlare default_gflare =
  539. {
  540.   NULL,        /* name */
  541.   NULL,        /* filename */
  542.   100,        /* glow_opacity */
  543.   GF_NORMAL,    /* glow_mode */
  544.   100,        /* rays_opacity */
  545.   GF_NORMAL,    /* rays_mode */
  546.   100,        /* sflare_opacity */
  547.   GF_NORMAL,    /* sflare_mode */
  548.   "%red_grad",    /* glow_radial */
  549.   "%white",    /* glow_angular */
  550.   "%white",    /* glow_angular_size */
  551.   100.0,    /* glow_size */
  552.   0.0,        /* glow_rotation */
  553.   0.0,        /* glow_hue */
  554.   "%white_grad",/* rays_radial */
  555.   "%random",    /* rays_angular */
  556.   "%random",    /* rays_angular_size */
  557.   100.0,    /* rays_size */
  558.   0.0,        /* rays_rotation */
  559.   0.0,        /* rays_hue */
  560.   40,        /* rays_nspikes */
  561.   20.0,        /* rays_thickness */
  562.   "%white_grad",/* sflare_radial */
  563.   "%random",    /* sflare_sizefac */
  564.   "%random",    /* sflare_probability */
  565.   40.0,        /* sflare_size */
  566.   0.0,        /* sflare_rotation */
  567.   0.0,        /* sflare_hue */
  568.   GF_CIRCLE,    /* sflare_shape */
  569.   6,        /* sflare_nverts */
  570.   1,        /* sflare_seed */
  571.   FALSE,        /* sflare_seed_time */
  572. };
  573.  
  574. /* These are keywords to be written to disk files specifying flares. */
  575. /* They are not translated since we want gflare files to be compatible
  576.    across languages. */
  577. static gchar *gflare_modes[] =
  578. {
  579.   "NORMAL",
  580.   "ADDITION",
  581.   "OVERLAY",
  582.   "SCREEN"
  583. };
  584.  
  585. static gchar *gflare_shapes[] =
  586. {
  587.   "CIRCLE",
  588.   "POLYGON"
  589. };
  590.  
  591. /* These are for menu entries, so they are translated. */
  592. static gchar *gflare_menu_modes[] =
  593. {
  594.   N_("Normal"),
  595.   N_("Addition"),
  596.   N_("Overlay"),
  597.   N_("Screen")
  598. };
  599.  
  600. static gint32              image_ID;
  601. static GimpDrawable       *drawable;
  602. static DrawableInfo        dinfo;
  603. static TileKeeper         *tk_read;
  604. static TileKeeper         *tk_write;
  605. static GFlareDialog       *dlg = NULL;
  606. static GFlareEditor       *ed = NULL;
  607. static GList              *gflares_list = NULL;
  608. static gint                num_gflares = 0;
  609. static GList              *gflare_path_list = NULL;
  610. static CalcParams          calc;
  611. static GList              *gradient_menus;
  612. static gchar             **gradient_names = NULL;
  613. static gint                num_gradient_names = 0;
  614. static GradientCacheItem  *gradient_cache_head  = NULL;
  615. static gint                gradient_cache_count = 0;
  616.  
  617.  
  618. static gchar *internal_gradients[] =
  619. {
  620.   "%white", "%white_grad", "%red_grad", "%blue_grad", "%yellow_grad", "%random"
  621. };
  622. static int internal_ngradients = (sizeof (internal_gradients) /
  623.                   sizeof (internal_gradients[0]));
  624.  
  625. #ifdef DEBUG
  626. static gint     get_values_external_count = 0;
  627. static clock_t  get_values_external_clock = 0;
  628. #endif
  629.  
  630.  
  631. /**
  632. ***    +++ Static Functions Prototypes
  633. **/
  634.  
  635. static void plugin_do                   (void);
  636.  
  637. static void plugin_do_non_asupsample    (void);
  638. static void plugin_do_asupsample        (void);
  639. static void plugin_render_func          (double x, double y,
  640.                      color_t *color, gpointer data);
  641. static void plugin_put_pixel_func       (int ix, int iy, color_t color,
  642.                      gpointer data);
  643. static void plugin_progress_func        (int y1, int y2, int curr_y,
  644.                      gpointer data);
  645.  
  646. static TileKeeper *tile_keeper_new      (gint shadow);
  647. static guchar *tile_keeper_provide      (TileKeeper *tk, gint ix, gint iy,
  648.                      gint dirty);
  649. static void tile_keeper_free            (TileKeeper *tk);
  650.  
  651. static GFlare * gflare_new              (void);
  652. static void gflare_free                 (GFlare *gflare);
  653. static void gflare_read_int             (gint *intvar, GFlareFile *gf);
  654. static void gflare_read_double          (gdouble *dblvar, GFlareFile *gf);
  655. static void gflare_read_gradient_name   (gchar *name, GFlareFile *gf);
  656. static void gflare_read_shape           (GFlareShape *shape, GFlareFile *gf);
  657. static void gflare_read_mode            (GFlareMode *mode, GFlareFile *gf);
  658. static void gflare_write_gradient_name  (gchar *name, FILE *fp);
  659.  
  660. static gint calc_sample_one_gradient    (void);
  661. static void calc_place_sflare           (void);
  662. static void calc_get_gradient           (guchar *pix, guchar *gradient,
  663.                      gdouble pos);
  664. static gdouble fmod_positive            (gdouble x, gdouble m);
  665. static void calc_paint_func             (guchar *dest,
  666.                      guchar *src1, guchar *src2,
  667.                      gint opacity, GFlareMode mode);
  668. static void calc_combine                (guchar *dest,
  669.                      guchar *src1, guchar *src2,
  670.                      gint opacity);
  671. static void calc_addition               (guchar *dest,
  672.                      guchar *src1, guchar *src2);
  673. static void calc_screen                 (guchar *dest,
  674.                      guchar *src1, guchar *src2);
  675. static void calc_overlay                (guchar *dest,
  676.                       guchar *src1, guchar *src2);
  677.  
  678. static void dlg_ok_callback             (GtkWidget *widget, gpointer data);
  679. static void dlg_setup_gflare            (void);
  680. static void dlg_page_map_callback       (GtkWidget *widget, gpointer data);
  681. static gint dlg_preview_handle_event    (GtkWidget *widget, GdkEvent *event);
  682. static gint ed_preview_handle_event     (GtkWidget *widget, GdkEvent *event);
  683. static void dlg_preview_update          (void);
  684. static gint dlg_preview_init_func       (Preview *preview, gpointer data);
  685. static void dlg_preview_render_func     (Preview *preview,
  686.                      guchar *dest, gint y, gpointer data);
  687. static void dlg_preview_deinit_func     (Preview *preview, gpointer data);
  688. static void dlg_make_page_settings      (GFlareDialog *dlg, GtkWidget *notebook);
  689. static void dlg_position_entry_callback (GtkWidget *widget, gpointer data);
  690. static void dlg_update_preview_callback (GtkWidget *widget, gpointer data);
  691. static void dlg_make_page_selector      (GFlareDialog *dlg, GtkWidget *notebook);
  692.  
  693. static void dlg_selector_setup_listbox      (void);
  694. static void dlg_selector_insert             (GFlare    *gflare,
  695.                          gint       pos,
  696.                          gint       select);
  697. static void dlg_selector_list_item_callback (GtkWidget *widget,
  698.                          gpointer   data);
  699.  
  700. static void dlg_selector_new_callback       (GtkWidget *widget,
  701.                          gpointer   data);
  702. static void dlg_selector_new_ok_callback    (GtkWidget *widget,
  703.                          gchar     *new_name,
  704.                          gpointer   data);
  705.  
  706. static void dlg_selector_edit_callback      (GtkWidget *widget,
  707.                          gpointer   data);
  708. static void dlg_selector_edit_done_callback (gint       updated,
  709.                          gpointer   data);
  710.  
  711. static void dlg_selector_copy_callback      (GtkWidget *widget,
  712.                          gpointer   data);
  713. static void dlg_selector_copy_ok_callback   (GtkWidget *widget,
  714.                          gchar     *copy_name,
  715.                          gpointer   data);
  716.  
  717. static void dlg_selector_delete_callback    (GtkWidget *widget,
  718.                          gpointer   data);
  719. static void dlg_selector_do_delete_callback (GtkWidget *widget,
  720.                          gboolean   delete,
  721.                          gpointer   data);
  722.  
  723. static void ed_run                (GFlare *target_gflare,
  724.                    GFlareEditorCallback callback,
  725.                    gpointer calldata);
  726. static void ed_close_callback     (GtkWidget *widget, gpointer data);
  727. static void ed_ok_callback        (GtkWidget *widget, gpointer data);
  728. static void ed_rescan_callback    (GtkWidget *widget, gpointer data);
  729. static void ed_make_page_general  (GFlareEditor *ed, GtkWidget *notebook);
  730. static void ed_make_page_glow     (GFlareEditor *ed, GtkWidget *notebook);
  731. static void ed_make_page_rays     (GFlareEditor *ed, GtkWidget *notebook);
  732. static void ed_make_page_sflare   (GFlareEditor *ed, GtkWidget *notebook);
  733. static void ed_put_gradient_menu  (GtkWidget *table, gint x, gint y,
  734.                    gchar *caption, GradientMenu *gm);
  735. static void ed_mode_menu_callback (GtkWidget *widget, gpointer data);
  736. static void ed_gradient_menu_callback (gchar *gradient_name, gpointer data);
  737. static void ed_shape_radio_callback   (GtkWidget *widget, gpointer data);
  738. static void ed_ientry_callback        (GtkWidget *widget, gpointer data);
  739. static void ed_page_map_callback      (GtkWidget *widget, gpointer data);
  740. static void ed_preview_update         (void);
  741. static gint ed_preview_init_func      (Preview *preview, gpointer data);
  742. static void ed_preview_deinit_func    (Preview *preview, gpointer data);
  743. static void ed_preview_render_func    (Preview *preview,
  744.                        guchar *buffer, gint y, gpointer data);
  745. static void ed_preview_render_general (guchar *buffer, gint y);
  746. static void ed_preview_render_glow    (guchar *buffer, gint y);
  747. static void ed_preview_render_rays    (guchar *buffer, gint y);
  748. static void ed_preview_render_sflare  (guchar *buffer, gint y);
  749.  
  750. static gint preview_render_start_2    (Preview *preview);
  751. static gint preview_handle_idle       (Preview *preview);
  752.  
  753. static void gm_gradient_get_list            (void);
  754. static GtkWidget *gm_menu_new               (GradientMenu *gm,
  755.                          gchar *default_gradient_name);
  756. static GtkWidget *gm_menu_create_sub_menus  (GradientMenu *gm, gint start_n,
  757.                          gchar **active_name_ptr,
  758.                          gchar *default_gradient_name);
  759. static void gm_menu_item_callback           (GtkWidget *w, gpointer data);
  760. static void gm_preview_draw                 (GtkWidget *preview,
  761.                          gchar *gradient_name);
  762. static void gm_option_menu_destroy_callback (GtkWidget *w, gpointer data);
  763.  
  764. static void gradient_get_values_internal    (gchar *gradient_name,
  765.                          guchar *values, gint nvalues);
  766. static void gradient_get_blend              (guchar *fg, guchar *bg,
  767.                          guchar *values, gint nvalues);
  768. static void gradient_get_random             (gint seed, guchar *values,
  769.                          gint nvalues);
  770. static void gradient_get_default            (gchar *name, guchar *values,
  771.                          gint nvalues);
  772. static void gradient_get_values_external    (gchar *gradient_name,
  773.                          guchar *values, gint nvalues);
  774. static void gradient_get_values_real_external   (gchar *gradient_name,
  775.                          guchar *values, gint nvalues);
  776. static GradientCacheItem *gradient_cache_lookup (gchar *name, gint *found);
  777. static void gradient_cache_zorch            (void);
  778.  
  779. /* *** INSERT-FILE-END *** */
  780.  
  781.  
  782. /*************************************************************************/
  783. /**                                    **/
  784. /**        +++ Plug-in Interfaces                    **/
  785. /**                                    **/
  786. /*************************************************************************/
  787.  
  788. MAIN ();
  789.  
  790. void
  791. plugin_query (void)
  792. {
  793.   static GimpParamDef args[]=
  794.   {
  795.     { GIMP_PDB_INT32,    "run_mode", "Interactive, non-interactive" },
  796.     { GIMP_PDB_IMAGE,    "image", "Input image (unused)" },
  797.     { GIMP_PDB_DRAWABLE, "drawable", "Input drawable" },
  798.     { GIMP_PDB_STRING,   "gflare_name", "The name of GFlare" },
  799.     { GIMP_PDB_INT32,    "xcenter", "X coordinate of center of GFlare" },
  800.     { GIMP_PDB_INT32,    "ycenter", "Y coordinate of center of GFlare" },
  801.     { GIMP_PDB_FLOAT,    "radius",    "Radius of GFlare (pixel)" },
  802.     { GIMP_PDB_FLOAT,    "rotation", "Rotation of GFlare (degree)" },
  803.     { GIMP_PDB_FLOAT,    "hue", "Hue rotation of GFlare (degree)" },
  804.     { GIMP_PDB_FLOAT,    "vangle", "Vector angle for second flares (degree)" },
  805.     { GIMP_PDB_FLOAT,    "vlength", "Vector length for second flares (percentage to Radius)" },
  806.     { GIMP_PDB_INT32,    "use_asupsample", "Whether it uses or not adaptive supersampling while rendering (boolean)" },
  807.     { GIMP_PDB_INT32,    "asupsample_max_depth", "Max depth for adaptive supersampling"},
  808.     { GIMP_PDB_FLOAT,   "asupsample_threshold", "Threshold for adaptive supersampling"}
  809.   };
  810.   static gint nargs = sizeof (args) / sizeof (args[0]);
  811.  
  812.   gchar     *help_string =
  813.     " This plug-in produces a lense flare effect using custom gradients."
  814.     " In interactive call, the user can edit his/her own favorite lense flare"
  815.     " (GFlare) and render it. Edited gflare is saved automatically to"
  816.     " the directory in gflare-path, if it is defined in gimprc."
  817.     " In non-interactive call, the user can only render one of GFlare"
  818.     " which has been stored in gflare-path already.";
  819.  
  820.   gimp_install_procedure ("plug_in_gflare",
  821.               "Produce lense flare effect using custom gradients",
  822.               help_string,
  823.               "Eiichi Takamori",
  824.               "Eiichi Takamori, and a lot of GIMP people",
  825.               "1997",
  826.               N_("<Image>/Filters/Light Effects/GFlare..."),
  827.               "RGB*, GRAY*",
  828.               GIMP_PLUGIN,
  829.               nargs, 0,
  830.               args, NULL);
  831. }
  832.  
  833. void
  834. plugin_run (gchar      *name,
  835.         gint        nparams,
  836.         GimpParam  *param,
  837.         gint       *nreturn_vals,
  838.         GimpParam **return_vals)
  839. {
  840.   static GimpParam  values[1];
  841.   GimpRunModeType   run_mode;
  842.   GimpPDBStatusType status = GIMP_PDB_SUCCESS;
  843.  
  844.   /* Initialize */
  845.   run_mode = param[0].data.d_int32;
  846.  
  847.   *nreturn_vals = 1;
  848.   *return_vals = values;
  849.  
  850.   values[0].type = GIMP_PDB_STATUS;
  851.   values[0].data.d_status = status;
  852.  
  853.   /*
  854.    *    Get the specified drawable and its info (global variable)
  855.    */
  856.  
  857.   image_ID = param[1].data.d_image;
  858.   drawable = gimp_drawable_get (param[2].data.d_drawable);
  859.   dinfo.is_color  = gimp_drawable_is_rgb (drawable->id);
  860.   dinfo.has_alpha = gimp_drawable_has_alpha (drawable->id);
  861.   gimp_drawable_mask_bounds (drawable->id, &dinfo.x1, &dinfo.y1,
  862.                  &dinfo.x2, &dinfo.y2);
  863.   dinfo.tile_width = gimp_tile_width ();
  864.   dinfo.tile_height = gimp_tile_height ();
  865.  
  866.   /*
  867.    *    Start gradient caching
  868.    */
  869.  
  870.   gradient_init ();
  871.  
  872.   /*
  873.    *    Parse gflare path from gimprc and load gflares
  874.    */
  875.  
  876.   plug_in_parse_gflare_path ();
  877.   gflares_list_load_all ();
  878.  
  879.   gimp_tile_cache_ntiles (drawable->width / gimp_tile_width () + 2);
  880.  
  881.  
  882.   switch (run_mode)
  883.     {
  884.     case GIMP_RUN_INTERACTIVE:
  885.       INIT_I18N_UI();
  886.  
  887.       /*  Possibly retrieve data  */
  888.       gimp_get_data ("plug_in_gflare", &pvals);
  889.  
  890.       /*  First acquire information with a dialog  */
  891.       if (! dlg_run ())
  892.     {
  893.       gimp_drawable_detach (drawable);
  894.       return;
  895.     }
  896.       break;
  897.  
  898.     case GIMP_RUN_NONINTERACTIVE:
  899. #if 0
  900.       printf("Currently non interactive call of gradient flare is not supported\n");
  901.       status = GIMP_PDB_CALLING_ERROR;
  902.       break;
  903. #endif
  904.       if (nparams != 14)
  905.     {
  906.       status = GIMP_PDB_CALLING_ERROR;
  907.     }
  908.       else
  909.     {
  910.       gflare_name_copy (pvals.gflare_name, param[3].data.d_string);
  911.       pvals.xcenter             = param[4].data.d_int32;
  912.       pvals.ycenter             = param[5].data.d_int32;
  913.       pvals.radius             = param[6].data.d_float;
  914.       pvals.rotation         = param[7].data.d_float;
  915.       pvals.hue             = param[8].data.d_float;
  916.       pvals.vangle             = param[9].data.d_float;
  917.       pvals.vlength             = param[10].data.d_float;
  918.       pvals.use_asupsample         = param[11].data.d_int32;
  919.       pvals.asupsample_max_depth = param[12].data.d_int32;
  920.       pvals.asupsample_threshold = param[13].data.d_float;
  921.  
  922.       if (pvals.radius <= 0)
  923.         status = GIMP_PDB_CALLING_ERROR;
  924.     }
  925.       break;
  926.  
  927.     case GIMP_RUN_WITH_LAST_VALS:
  928.       /*  Possibly retrieve data  */
  929.       gimp_get_data ("plug_in_gflare", &pvals);
  930.       break;
  931.  
  932.     default:
  933.       break;
  934.     }
  935.  
  936.   if (status == GIMP_PDB_SUCCESS)
  937.     {
  938.       /*  Make sure that the drawable is gray or RGB color  */
  939.       if (gimp_drawable_is_rgb (drawable->id) ||
  940.       gimp_drawable_is_gray (drawable->id))
  941.     {
  942.       gimp_progress_init (_("Gradient Flare..."));
  943.       plugin_do ();
  944.  
  945.       if (run_mode != GIMP_RUN_NONINTERACTIVE)
  946.         gimp_displays_flush ();
  947.  
  948.       /*  Store data  */
  949.       if (run_mode == GIMP_RUN_INTERACTIVE)
  950.         gimp_set_data ("plug_in_gflare", &pvals, sizeof (PluginValues));
  951.     }
  952.       else
  953.     {
  954.       g_message (_("GFlare: cannot operate on indexed color images"));
  955.       status = GIMP_PDB_EXECUTION_ERROR;
  956.     }
  957.     }
  958.  
  959.   values[0].data.d_status = status;
  960.  
  961.   /*
  962.    *    Deinitialization
  963.    */
  964.   gradient_free ();
  965.   gimp_drawable_detach (drawable);
  966. }
  967.  
  968. /*
  969.  *    Query gimprc for gflare-path, and parse it.
  970.  *    This code is based on script_fu_find_scripts ()
  971.  */
  972. void
  973. plug_in_parse_gflare_path (void)
  974. {
  975.   gchar *gflare_path;  
  976.  
  977.   GList *fail_list = NULL;
  978.   GList *list;
  979.  
  980.   gimp_path_free (gflare_path_list);
  981.   gflare_path_list = NULL;
  982.  
  983.   gflare_path = gimp_gimprc_query ("gflare-path");
  984.  
  985.   if (!gflare_path)
  986.     {
  987.       gchar *gimprc = gimp_personal_rc_file ("gimprc");
  988.       gchar *path   = gimp_strescape ("${gimp_dir}" G_DIR_SEPARATOR_S "gflare"
  989.                       G_SEARCHPATH_SEPARATOR_S
  990.                       "${gimp_data_dir}" G_DIR_SEPARATOR_S "gflare",
  991.                       NULL);
  992.       g_message (_("No gflare-path in gimprc:\n"
  993.            "You need to add an entry like\n"
  994.            "(gflare-path \"%s\")\n"
  995.            "to your %s file."), path, gimprc);
  996.       g_free (gimprc);
  997.       g_free (path);
  998.  
  999.       return;
  1000.     }
  1001.  
  1002.  
  1003.   gflare_path_list = gimp_path_parse (gflare_path,
  1004.                       16, TRUE, &fail_list);
  1005.   g_free (gflare_path);
  1006.  
  1007.   if (fail_list)
  1008.     {
  1009.       GString *err =
  1010.         g_string_new (_("gflare-path misconfigured - "
  1011.                         "the following directories were not found"));
  1012.  
  1013.       for (list = fail_list; list; list = g_list_next (list))
  1014.         {
  1015.           g_string_append_c (err, '\n');
  1016.           g_string_append (err, (gchar *) list->data);
  1017.         }
  1018.  
  1019.       g_message (err->str);
  1020.  
  1021.       g_string_free (err, TRUE);
  1022.       gimp_path_free (fail_list);
  1023.     }
  1024. }
  1025.  
  1026.  
  1027. static void
  1028. plugin_do (void)
  1029. {
  1030.   GFlare *gflare;
  1031.  
  1032.   gflare = gflares_list_lookup (pvals.gflare_name);
  1033.   if (gflare == NULL)
  1034.     {
  1035.       /* FIXME */
  1036.       g_warning ("Not found %s\n", pvals.gflare_name);
  1037.       return;
  1038.     }
  1039.  
  1040.   /* Initialize calc params and gradients */
  1041.   calc_init_params (gflare, CALC_GLOW | CALC_RAYS | CALC_SFLARE,
  1042.             pvals.xcenter, pvals.ycenter,
  1043.             pvals.radius, pvals.rotation, pvals.hue,
  1044.             pvals.vangle, pvals.vlength);
  1045.   while (calc_init_progress ()) ;
  1046.  
  1047.   /* Render it ! */
  1048.   if (pvals.use_asupsample)
  1049.     plugin_do_asupsample ();
  1050.   else
  1051.     plugin_do_non_asupsample ();
  1052.  
  1053.   /* Clean up */
  1054.   calc_deinit ();
  1055.   gimp_drawable_flush (drawable);
  1056.   gimp_drawable_merge_shadow (drawable->id, TRUE);
  1057.   gimp_drawable_update (drawable->id, dinfo.x1, dinfo.y1,
  1058.             (dinfo.x2 - dinfo.x1), (dinfo.y2 - dinfo.y1));
  1059. }
  1060.  
  1061. /* these routines should be almost rewritten anyway */
  1062.  
  1063. static void
  1064. plugin_do_non_asupsample (void)
  1065. {
  1066.   GimpPixelRgn  src_rgn, dest_rgn;
  1067.   gpointer      pr;
  1068.   guchar       *src_row;
  1069.   guchar       *dest_row;
  1070.   guchar       *src;
  1071.   guchar       *dest;
  1072.   gint          row, col;
  1073.   gint          x, y;
  1074.   gint          b;
  1075.   gint          progress, max_progress;
  1076.   guchar        src_pix[4];
  1077.   guchar        dest_pix[4];
  1078.  
  1079.   progress = 0;
  1080.   max_progress = (dinfo.x2 - dinfo.x1) * (dinfo.y2 - dinfo.y1);
  1081.  
  1082.   gimp_pixel_rgn_init (&src_rgn, drawable,
  1083.                dinfo.x1, dinfo.y1, dinfo.x2, dinfo.y2, FALSE, FALSE);
  1084.   gimp_pixel_rgn_init (&dest_rgn, drawable,
  1085.                dinfo.x1, dinfo.y1, dinfo.x2, dinfo.y2, TRUE, TRUE);
  1086.  
  1087.   for (pr = gimp_pixel_rgns_register (2, &src_rgn, &dest_rgn);
  1088.        pr != NULL; pr = gimp_pixel_rgns_process (pr))
  1089.     {
  1090.       src_row  = src_rgn.data;
  1091.       dest_row = dest_rgn.data;
  1092.  
  1093.       for (row = 0, y = src_rgn.y; row < src_rgn.h; row++, y++)
  1094.     {
  1095.       src = src_row;
  1096.       dest = dest_row;
  1097.  
  1098.       for (col = 0, x = src_rgn.x; col < src_rgn.w; col++, x++)
  1099.         {
  1100.           for (b = 0; b < 3; b++)
  1101.         src_pix[b] = dinfo.is_color ? src[b] : src[0];
  1102.           src_pix[3] = dinfo.has_alpha ? src[src_rgn.bpp - 1] : OPAQUE;
  1103.  
  1104.           calc_gflare_pix (dest_pix, x, y, src_pix);
  1105.  
  1106.           if (dinfo.is_color)
  1107.         for (b = 0; b < 3; b++)
  1108.           dest[b] = dest_pix[b];
  1109.           else
  1110.         dest[0] = LUMINOSITY (dest_pix);
  1111.  
  1112.           if (dinfo.has_alpha)
  1113.         dest[src_rgn.bpp - 1] = dest_pix[3];
  1114.  
  1115.           src  += src_rgn.bpp;
  1116.           dest += dest_rgn.bpp;
  1117.         }
  1118.       src_row  += src_rgn.rowstride;
  1119.       dest_row += dest_rgn.rowstride;
  1120.     }
  1121.       /* Update progress */
  1122.       progress += src_rgn.w * src_rgn.h;
  1123.       gimp_progress_update ((double) progress / (double) max_progress);
  1124.     }
  1125. }
  1126.  
  1127. static void
  1128. plugin_do_asupsample ()
  1129. {
  1130.   tk_read  = tile_keeper_new (FALSE);
  1131.   tk_write = tile_keeper_new (TRUE);
  1132.  
  1133.   adaptive_supersample_area (dinfo.x1, dinfo.y1, dinfo.x2 - 1, dinfo.y2 - 1,
  1134.                  pvals.asupsample_max_depth,
  1135.                  pvals.asupsample_threshold,
  1136.                  (render_func_t) plugin_render_func,
  1137.                  NULL,
  1138.                  (put_pixel_func_t) plugin_put_pixel_func,
  1139.                  NULL,
  1140.                  (progress_func_t) plugin_progress_func,
  1141.                  NULL);
  1142.   tile_keeper_free (tk_read);
  1143.   tile_keeper_free (tk_write);
  1144. }
  1145.  
  1146. /*
  1147.   Adaptive supersampling callback functions
  1148.  
  1149.   These routines may look messy, since adaptive supersampling needs
  1150.   pixel values in `double' (from 0.0 to 1.0) but calc_*_pix () returns
  1151.   guchar values. */
  1152.  
  1153. static void
  1154. plugin_render_func (double x, double y, color_t *color, gpointer data)
  1155. {
  1156.   guchar    src_pix[4];
  1157.   guchar    flare_pix[4];
  1158.   guchar    *src;
  1159.   gint        b;
  1160.   gint        ix, iy;
  1161.  
  1162.   /* translate (0.5, 0.5) before convert to `int' so that it can surely
  1163.      point the center of pixel */
  1164.   ix = floor (x + 0.5);
  1165.   iy = floor (y + 0.5);
  1166.  
  1167.   src = tile_keeper_provide (tk_read, ix, iy, FALSE);
  1168.  
  1169.   for (b = 0; b < 3; b++)
  1170.     src_pix[b] = dinfo.is_color ? src[b] : src[0];
  1171.   src_pix[3] = dinfo.has_alpha ? src[drawable->bpp - 1] : OPAQUE;
  1172.  
  1173.   calc_gflare_pix (flare_pix, x, y, src_pix);
  1174.  
  1175.   color->r = flare_pix[0] / 255.0;
  1176.   color->g = flare_pix[1] / 255.0;
  1177.   color->b = flare_pix[2] / 255.0;
  1178.   color->a = flare_pix[3] / 255.0;
  1179. }
  1180.  
  1181. static void
  1182. plugin_put_pixel_func (int ix, int iy, color_t color, gpointer data)
  1183. {
  1184.   guchar    *dest;
  1185.  
  1186.   dest = tile_keeper_provide (tk_write, ix, iy, TRUE);
  1187.  
  1188.   if (dinfo.is_color)
  1189.     {
  1190.       dest[0] = color.r * 255;
  1191.       dest[1] = color.g * 255;
  1192.       dest[2] = color.b * 255;
  1193.     }
  1194.   else
  1195.     dest[0] = INTENSITY (color.r, color.g, color.b) * 255;
  1196.  
  1197.   if (dinfo.has_alpha)
  1198.     dest[drawable->bpp - 1] = color.a * 255;
  1199. }
  1200.  
  1201. static void
  1202. plugin_progress_func (int y1, int y2, int curr_y, gpointer data)
  1203. {
  1204.   gimp_progress_update ((double) curr_y / (double) (y2 - y1));
  1205. }
  1206.  
  1207. /**
  1208. ***    The Tile Keeper
  1209. **/
  1210.  
  1211. static TileKeeper *
  1212. tile_keeper_new (gint shadow)
  1213. {
  1214.   TileKeeper    *tk;
  1215.  
  1216.   tk = g_new0 (TileKeeper, 1);
  1217.   tk->tile      = NULL;
  1218.   tk->col      = 0;
  1219.   tk->row      = 0;
  1220.   tk->shadow      = shadow;
  1221.   tk->dirty      = FALSE;
  1222.  
  1223.   return tk;
  1224. }
  1225.  
  1226. /*
  1227.   Return the pointer to specified pixel in allocated tile
  1228.   */
  1229. static guchar *
  1230. tile_keeper_provide (TileKeeper *tk, gint ix, gint iy, gint dirty)
  1231. {
  1232.   static guchar black[4];
  1233.   gint        col, row, offx, offy;
  1234.  
  1235.   if (ix < 0 || ix >= drawable->width || iy < 0 || iy >= drawable->height)
  1236.     {
  1237.       black[0] = black[1] = black[2] = black[3] = 0;
  1238.       return black;
  1239.     }
  1240.  
  1241.   col  = ix / dinfo.tile_width;
  1242.   row  = iy / dinfo.tile_height;
  1243.   offx = ix % dinfo.tile_width;
  1244.   offy = iy % dinfo.tile_height;
  1245.  
  1246.   if (tk->tile == NULL || col != tk->col || row != tk->row)
  1247.     {
  1248.       if (tk->tile)
  1249.     gimp_tile_unref (tk->tile, tk->dirty);
  1250.       tk->col    = col;
  1251.       tk->row    = row;
  1252.       tk->tile    = gimp_drawable_get_tile (drawable, tk->shadow, row, col);
  1253.       tk->dirty = FALSE;
  1254.       gimp_tile_ref (tk->tile);
  1255.     }
  1256.  
  1257.   tk->dirty |= dirty;
  1258.   return tk->tile->data + (offy * tk->tile->ewidth + offx) * tk->tile->bpp;
  1259. }
  1260.  
  1261. static void
  1262. tile_keeper_free (TileKeeper *tk)
  1263. {
  1264.   if (tk->tile)
  1265.     gimp_tile_unref (tk->tile, tk->dirty);
  1266.   g_free (tk);
  1267. }
  1268.  
  1269.  
  1270. /*************************************************************************/
  1271. /**                                    **/
  1272. /**        +++ GFlare Routines                    **/
  1273. /**                                    **/
  1274. /*************************************************************************/
  1275.  
  1276. /*
  1277.  *    These code are more or less based on Quartic's gradient.c,
  1278.  *    other gimp sources, and script-fu.
  1279.  */
  1280.  
  1281. static GFlare *
  1282. gflare_new ()
  1283. {
  1284.   GFlare    *gflare;
  1285.  
  1286.   gflare = g_new0 (GFlare, 1);
  1287.   gflare->name = NULL;
  1288.   gflare->filename = NULL;
  1289.   return gflare;
  1290. }
  1291.  
  1292. GFlare *
  1293. gflare_new_with_default (gchar *new_name)
  1294. {
  1295.   DEBUG_PRINT (("gflare_new_with_default %s\n", new_name));
  1296.  
  1297.   return gflare_dup (&default_gflare, new_name);
  1298. }
  1299.  
  1300. GFlare *
  1301. gflare_dup (GFlare *src, gchar *new_name)
  1302. {
  1303.   GFlare    *dest;
  1304.  
  1305.   DEBUG_PRINT (("gflare_dup %s\n", new_name));
  1306.  
  1307.   dest = g_new0 (GFlare, 1);
  1308.  
  1309.   memcpy (dest, src, sizeof(GFlare));
  1310.  
  1311.   dest->name = g_strdup (new_name);
  1312.   dest->filename = NULL;
  1313.  
  1314.   return dest;
  1315. }
  1316.  
  1317. void
  1318. gflare_copy (GFlare *dest, GFlare *src)
  1319. {
  1320.   gchar *name, *filename;
  1321.  
  1322.   DEBUG_PRINT (("gflare_copy\n"));
  1323.  
  1324.   name = dest->name;
  1325.   filename = dest->filename;
  1326.  
  1327.   memcpy (dest, src, sizeof (GFlare));
  1328.  
  1329.   dest->name = name;
  1330.   dest->filename =filename;
  1331. }
  1332.  
  1333.  
  1334. static void
  1335. gflare_free (GFlare *gflare)
  1336. {
  1337.   g_assert (gflare != NULL);
  1338.  
  1339.   if (gflare->name)
  1340.     g_free (gflare->name);
  1341.   if (gflare->filename)
  1342.     g_free (gflare->filename);
  1343.   g_free (gflare);
  1344. }
  1345.  
  1346. GFlare *
  1347. gflare_load (char *filename, char *name)
  1348. {
  1349.   FILE        *fp;
  1350.   GFlareFile    *gf;
  1351.   GFlare    *gflare;
  1352.   gchar        header[256];
  1353.  
  1354.   DEBUG_PRINT (("gflare_load: %s, %s\n", filename, name));
  1355.   g_assert (filename != NULL);
  1356.  
  1357.   fp = fopen (filename, "r");
  1358.   if (!fp)
  1359.     {
  1360.       g_warning ("not found: %s", filename);
  1361.       return NULL;
  1362.     }
  1363.  
  1364.   if (fgets (header, sizeof(header), fp) == NULL
  1365.       || strcmp (header, GFLARE_FILE_HEADER) != 0)
  1366.     {
  1367.       g_warning (_("not valid GFlare file: %s"), filename);
  1368.       fclose (fp);
  1369.       return NULL;
  1370.     }
  1371.  
  1372.   gf = g_new (GFlareFile, 1);
  1373.   gf->fp = fp;
  1374.   gf->error = FALSE;
  1375.  
  1376.   gflare = gflare_new ();
  1377.   gflare->name = g_strdup (name);
  1378.   gflare->filename = g_strdup (filename);
  1379.  
  1380.   gflare_read_double   (&gflare->glow_opacity, gf);
  1381.   gflare_read_mode     (&gflare->glow_mode, gf);
  1382.   gflare_read_double   (&gflare->rays_opacity, gf);
  1383.   gflare_read_mode     (&gflare->rays_mode, gf);
  1384.   gflare_read_double   (&gflare->sflare_opacity, gf);
  1385.   gflare_read_mode     (&gflare->sflare_mode, gf);
  1386.  
  1387.   gflare_read_gradient_name (gflare->glow_radial, gf);
  1388.   gflare_read_gradient_name (gflare->glow_angular, gf);
  1389.   gflare_read_gradient_name (gflare->glow_angular_size, gf);
  1390.   gflare_read_double   (&gflare->glow_size, gf);
  1391.   gflare_read_double   (&gflare->glow_rotation, gf);
  1392.   gflare_read_double   (&gflare->glow_hue, gf);
  1393.  
  1394.   gflare_read_gradient_name (gflare->rays_radial, gf);
  1395.   gflare_read_gradient_name (gflare->rays_angular, gf);
  1396.   gflare_read_gradient_name (gflare->rays_angular_size, gf);
  1397.   gflare_read_double   (&gflare->rays_size, gf);
  1398.   gflare_read_double   (&gflare->rays_rotation, gf);
  1399.   gflare_read_double   (&gflare->rays_hue, gf);
  1400.   gflare_read_int      (&gflare->rays_nspikes, gf);
  1401.   gflare_read_double   (&gflare->rays_thickness, gf);
  1402.  
  1403.   gflare_read_gradient_name (gflare->sflare_radial, gf);
  1404.   gflare_read_gradient_name (gflare->sflare_sizefac, gf);
  1405.   gflare_read_gradient_name (gflare->sflare_probability, gf);
  1406.   gflare_read_double   (&gflare->sflare_size, gf);
  1407.   gflare_read_double   (&gflare->sflare_hue, gf);
  1408.   gflare_read_double   (&gflare->sflare_rotation, gf);
  1409.   gflare_read_shape    (&gflare->sflare_shape, gf);
  1410.   gflare_read_int      (&gflare->sflare_nverts, gf);
  1411.   gflare_read_int      (&gflare->sflare_seed, gf);
  1412.  
  1413.   if (gflare->sflare_seed == -1)
  1414.     {
  1415.       gflare->sflare_seed = 1;
  1416.       gflare->sflare_seed_time = TRUE;
  1417.     }
  1418.   else
  1419.     {
  1420.       gflare->sflare_seed_time = FALSE;
  1421.     }
  1422.  
  1423.   fclose (gf->fp);
  1424.  
  1425.   if (gf->error)
  1426.     {
  1427.       g_warning (_("invalid formatted GFlare file: %s\n"), filename);
  1428.       g_free (gflare);
  1429.       g_free (gf);
  1430.       return NULL;
  1431.     }
  1432.  
  1433.   DEBUG_PRINT (("Loaded %s\n", filename));
  1434.   g_free (gf);
  1435.  
  1436.   return gflare;
  1437. }
  1438.  
  1439. static void
  1440. gflare_read_int (gint *intvar, GFlareFile *gf)
  1441. {
  1442.   if (gf->error)
  1443.     return;
  1444.  
  1445.   if (fscanf (gf->fp, "%d", intvar) != 1)
  1446.     gf->error = TRUE;
  1447. }
  1448.  
  1449. static void
  1450. gflare_read_double (gdouble *dblvar, GFlareFile *gf)
  1451. {
  1452.   if (gf->error)
  1453.     return;
  1454.  
  1455.   if (fscanf (gf->fp, "%le", dblvar) != 1)
  1456.     gf->error = TRUE;
  1457. }
  1458.  
  1459. static void
  1460. gflare_read_gradient_name (GradientName name, GFlareFile *gf)
  1461. {
  1462.   gchar        tmp[1024], dec[1024];
  1463.  
  1464.   if (gf->error)
  1465.     return;
  1466.  
  1467.   /* FIXME: this is buggy */
  1468.  
  1469.   if (fscanf (gf->fp, "%s", tmp) == 1)
  1470.     {
  1471.       /* @GRADIENT_NAME */
  1472.       gradient_name_decode ((guchar*) dec, (guchar*) tmp);
  1473.       gradient_name_copy (name, tmp);
  1474.       DEBUG_PRINT (("read_gradient_name: \"%s\" => \"%s\"\n", tmp, dec));
  1475.     }
  1476.   else
  1477.     gf->error = TRUE;
  1478. }
  1479.  
  1480. static void
  1481. gflare_read_shape (GFlareShape *shape, GFlareFile *gf)
  1482. {
  1483.   gchar tmp[1024];
  1484.   gint    i;
  1485.  
  1486.   if (gf->error)
  1487.     return;
  1488.  
  1489.   if (fscanf (gf->fp, "%s", tmp) == 1)
  1490.     {
  1491.       for (i = 0; i < GF_NUM_SHAPES; i++)
  1492.     if (strcmp (tmp, gflare_shapes[i]) == 0)
  1493.       {
  1494.         *shape = i;
  1495.         return;
  1496.       }
  1497.     }
  1498.   gf->error = TRUE;
  1499. }
  1500.  
  1501. static void
  1502. gflare_read_mode (GFlareMode *mode, GFlareFile *gf)
  1503. {
  1504.   gchar tmp[1024];
  1505.   gint    i;
  1506.  
  1507.   if (gf->error)
  1508.     return;
  1509.  
  1510.   if (fscanf (gf->fp, "%s", tmp) == 1)
  1511.     {
  1512.       for (i = 0; i < GF_NUM_MODES; i++)
  1513.     if (strcmp (tmp, gflare_modes[i]) == 0)
  1514.       {
  1515.         *mode = i;
  1516.         return;
  1517.       }
  1518.     }
  1519.   gf->error = TRUE;
  1520. }
  1521.  
  1522. void
  1523. gflare_save (GFlare *gflare)
  1524. {
  1525.   FILE    *fp;
  1526.   gchar *path;
  1527.   static gboolean message_ok = FALSE;
  1528.  
  1529.   if (gflare->filename == NULL)
  1530.     {
  1531.       if (gflare_path_list == NULL)
  1532.     {
  1533.       if (!message_ok)
  1534.         {
  1535.           gchar *gimprc = gimp_personal_rc_file ("gimprc");
  1536.           gchar *gflare_path = gimp_strescape
  1537.         ("${gimp_dir}" G_DIR_SEPARATOR_S "gflare",
  1538.          NULL);
  1539.           gchar *dir = gimp_personal_rc_file ("gflare");
  1540.  
  1541.           g_message (_("GFlare `%s' is not saved.\n"
  1542.                "If you add a new entry in %s, like:\n"
  1543.                "(gflare-path \"%s\")\n"
  1544.                "and make a directory %s,\n"
  1545.                "then you can save your own GFlare's into that directory."), 
  1546.              gflare->name, gimprc, gflare_path, dir);
  1547.           g_free (gimprc);
  1548.           g_free (gflare_path);
  1549.           g_free (dir);
  1550.           message_ok = TRUE;
  1551.         }          
  1552.       return;
  1553.     }
  1554.  
  1555.       path = gimp_path_get_user_writable_dir (gflare_path_list);
  1556.  
  1557.       if (!path)
  1558.     path = g_strdup (gimp_directory ());
  1559.  
  1560.       gflare->filename = g_strdup_printf ("%s%s", path, gflare->name);
  1561.  
  1562.       g_free (path);
  1563.     }
  1564.  
  1565.   fp = fopen (gflare->filename, "w");
  1566.   if (!fp)
  1567.     {
  1568.       g_warning (_("could not open \"%s\""), gflare->filename);
  1569.       return;
  1570.     }
  1571.  
  1572.   fprintf (fp, "%s", GFLARE_FILE_HEADER);
  1573.   fprintf (fp, "%f %s\n", gflare->glow_opacity, gflare_modes[gflare->glow_mode]);
  1574.   fprintf (fp, "%f %s\n", gflare->rays_opacity, gflare_modes[gflare->rays_mode]);
  1575.   fprintf (fp, "%f %s\n", gflare->sflare_opacity, gflare_modes[gflare->sflare_mode]);
  1576.  
  1577.   gflare_write_gradient_name (gflare->glow_radial, fp);
  1578.   gflare_write_gradient_name (gflare->glow_angular, fp);
  1579.   gflare_write_gradient_name (gflare->glow_angular_size, fp);
  1580.   fprintf (fp, "%f %f %f\n", gflare->glow_size, gflare->glow_rotation, gflare->glow_hue);
  1581.  
  1582.   gflare_write_gradient_name (gflare->rays_radial, fp);
  1583.   gflare_write_gradient_name (gflare->rays_angular, fp);
  1584.   gflare_write_gradient_name (gflare->rays_angular_size, fp);
  1585.   fprintf (fp, "%f %f %f\n", gflare->rays_size, gflare->rays_rotation, gflare->rays_hue);
  1586.   fprintf (fp, "%d %f\n", gflare->rays_nspikes, gflare->rays_thickness);
  1587.  
  1588.   gflare_write_gradient_name (gflare->sflare_radial, fp);
  1589.   gflare_write_gradient_name (gflare->sflare_sizefac, fp);
  1590.   gflare_write_gradient_name (gflare->sflare_probability, fp);
  1591.   fprintf (fp, "%f %f %f\n", gflare->sflare_size, gflare->sflare_rotation, gflare->sflare_hue);
  1592.   fprintf (fp, "%s %d %d\n", gflare_shapes[gflare->sflare_shape], gflare->sflare_nverts, gflare->sflare_seed_time ? -1 : gflare->sflare_seed);
  1593.  
  1594.   fclose (fp);
  1595.   DEBUG_PRINT (("Saved %s\n", gflare->filename));
  1596. }
  1597.  
  1598. static void
  1599. gflare_write_gradient_name (GradientName name, FILE *fp)
  1600. {
  1601.   gchar        enc[1024];
  1602.  
  1603.   /* @GRADIENT_NAME */
  1604.  
  1605.   /* encode white spaces and control characters (if any) */
  1606.   gradient_name_encode ((guchar*) enc, (guchar*) name);
  1607.  
  1608.   fprintf (fp, "%s\n", enc);
  1609.   DEBUG_PRINT (("write_gradient_name: \"%s\" => \"%s\"\n", name, enc));
  1610. }
  1611.  
  1612. void
  1613. gflare_name_copy (gchar *dest, gchar *src)
  1614. {
  1615.   strncpy (dest, src, GFLARE_NAME_MAX);
  1616.   dest[GFLARE_NAME_MAX-1] = '\0';
  1617. }
  1618.  
  1619.  
  1620. /*************************************************************************/
  1621. /**                                    **/
  1622. /**        +++ GFlares List                    **/
  1623. /**                                    **/
  1624. /*************************************************************************/
  1625.  
  1626. gint
  1627. gflares_list_insert (GFlare *gflare)
  1628. {
  1629.   GList        *tmp;
  1630.   GFlare    *g;
  1631.   int        n;
  1632.  
  1633.   /*
  1634.    *    Insert gflare in alphabetical order
  1635.    */
  1636.  
  1637.   n = 0;
  1638.   tmp = gflares_list;
  1639.  
  1640.   while (tmp)
  1641.     {
  1642.       g = tmp->data;
  1643.  
  1644.       if (strcmp (gflare->name, g->name) <= 0)
  1645.     break;
  1646.       n++;
  1647.       tmp = tmp->next;
  1648.   }
  1649.  
  1650.   num_gflares++;
  1651.   gflares_list = g_list_insert (gflares_list, gflare, n);
  1652.  
  1653.   DEBUG_PRINT (("gflares_list_insert %s => %d\n", gflare->name, n));
  1654.  
  1655.   return n;
  1656. }
  1657.  
  1658. GFlare *
  1659. gflares_list_lookup (gchar *name)
  1660. {
  1661.   GList        *tmp;
  1662.   GFlare    *gflare;
  1663.  
  1664.   DEBUG_PRINT (("gflares_list_lookup %s\n", name));
  1665.  
  1666.   tmp = gflares_list;
  1667.   while (tmp)
  1668.     {
  1669.       gflare = tmp->data;
  1670.       tmp = tmp->next;
  1671.       if (strcmp (gflare->name, name) == 0)
  1672.     return gflare;
  1673.     }
  1674.   return NULL;
  1675. }
  1676.  
  1677. gint
  1678. gflares_list_index (GFlare *gflare)
  1679. {
  1680.   GList        *tmp;
  1681.   gint        n;
  1682.  
  1683.   DEBUG_PRINT (("gflares_list_index %s\n", gflare->name));
  1684.  
  1685.   n = 0;
  1686.   tmp = gflares_list;
  1687.   while (tmp)
  1688.     {
  1689.       if (tmp->data == gflare)
  1690.     return n;
  1691.       tmp = tmp->next;
  1692.       n++;
  1693.     }
  1694.   return -1;
  1695. }
  1696.  
  1697. gint
  1698. gflares_list_remove (GFlare *gflare)
  1699. {
  1700.   GList        *tmp;
  1701.   gint        n;
  1702.  
  1703.   DEBUG_PRINT (("gflares_list_remove %s\n", gflare->name));
  1704.  
  1705.   n = 0;
  1706.   tmp = gflares_list;
  1707.   while (tmp)
  1708.     {
  1709.       if (tmp->data == gflare)
  1710.     {
  1711.       /* Found! */
  1712.       if (tmp->next == NULL)
  1713.       num_gflares--;
  1714.       gflares_list = g_list_remove (gflares_list, gflare);
  1715.       return n;
  1716.     }
  1717.       tmp = tmp->next;
  1718.       n++;
  1719.     }
  1720.   return -1;
  1721. }
  1722.  
  1723. /*
  1724.   Load all gflares, which are founded in gflare-path-list, into gflares_list.
  1725.  
  1726.   gflares-path-list must be initialized first. (plug_in_parse_gflare_path ())
  1727.  */
  1728. void
  1729. gflares_list_load_all (void)
  1730. {
  1731.   GFlare    *gflare;
  1732.   GList        *list;
  1733.   gchar        *path;
  1734.   gchar        *filename;
  1735.   DIR        *dir;
  1736.   struct dirent *dir_ent;
  1737.   struct stat    filestat;
  1738.   gint        err;
  1739.  
  1740. #if 0    /* @@@ */
  1741.   printf("Waiting... (pid %d)\n", getpid());
  1742.   kill(getpid(), 19); /* SIGSTOP */
  1743. #endif
  1744.  
  1745.   /*  Make sure to clear any existing gflares  */
  1746.   gflares_list_free_all ();
  1747.  
  1748.   list = gflare_path_list;
  1749.   while (list)
  1750.     {
  1751.       path = list->data;
  1752.       list = list->next;
  1753.  
  1754.       /* Open directory */
  1755.       dir = opendir (path);
  1756.  
  1757.       if (!dir)
  1758.     g_warning(_("error reading GFlare directory \"%s\""), path);
  1759.       else
  1760.     {
  1761.       while ((dir_ent = readdir (dir)))
  1762.         {
  1763.           filename = g_strdup_printf ("%s%s", path, dir_ent->d_name);
  1764.  
  1765.           /* Check the file and see that it is not a sub-directory */
  1766.           err = stat (filename, &filestat);
  1767.  
  1768.           if (!err && S_ISREG (filestat.st_mode))
  1769.         {
  1770.           gflare = gflare_load (filename, dir_ent->d_name);
  1771.           if (gflare)
  1772.             gflares_list_insert (gflare);
  1773.         }
  1774.  
  1775.           g_free (filename);
  1776.         } /* while */
  1777.  
  1778.       closedir (dir);
  1779.     } /* else */
  1780.     }
  1781. }
  1782.  
  1783. void
  1784. gflares_list_free_all (void)
  1785. {
  1786.   GList *list;
  1787.   GFlare *gflare;
  1788.  
  1789.   list = gflares_list;
  1790.   while (list)
  1791.     {
  1792.       gflare = (GFlare *) list->data;
  1793.       gflare_free (gflare);
  1794.       list = list->next;
  1795.     }
  1796.  
  1797.   g_list_free (gflares_list);
  1798.   gflares_list = NULL;
  1799. }
  1800.  
  1801.  
  1802.  
  1803. /*************************************************************************/
  1804. /**                                    **/
  1805. /**        +++ Calculator                        **/
  1806. /**                                    **/
  1807. /*************************************************************************/
  1808.  
  1809.  
  1810. /*
  1811.  * These routines calculates pixel values of particular gflare, at
  1812.  * specified point.  The client which wants to get benefit from these
  1813.  * calculation routines must call calc_init_params() first, and
  1814.  * iterate calling calc_init_progress() until it returns FALSE. and
  1815.  * must call calc_deinit() when job is done.
  1816.  */
  1817.  
  1818. void
  1819. calc_init_params (GFlare *gflare, gint calc_type,
  1820.           gdouble xcenter, gdouble ycenter,
  1821.           gdouble radius, gdouble rotation, gdouble hue,
  1822.           gdouble vangle, gdouble vlength)
  1823. {
  1824.   DEBUG_PRINT (("//// calc_init_params ////\n"));
  1825.   calc.type       = calc_type;
  1826.   calc.gflare       = gflare;
  1827.   calc.xcenter       = xcenter;
  1828.   calc.ycenter       = ycenter;
  1829.   calc.radius       = radius;
  1830.   calc.rotation       = rotation * G_PI / 180.0;
  1831.   calc.hue       = hue;
  1832.   calc.vangle       = vangle * G_PI / 180.0;
  1833.   calc.vlength       = radius * vlength / 100.0;
  1834.   calc.glow_radius   = radius * gflare->glow_size / 100.0;
  1835.   calc.rays_radius   = radius * gflare->rays_size / 100.0;
  1836.   calc.sflare_radius = radius * gflare->sflare_size / 100.0;
  1837.   calc.glow_rotation = (rotation + gflare->glow_rotation) * G_PI / 180.0;
  1838.   calc.rays_rotation = (rotation + gflare->rays_rotation) * G_PI / 180.0;
  1839.   calc.sflare_rotation = (rotation + gflare->sflare_rotation) * G_PI / 180.0;
  1840.   calc.glow_opacity  = gflare->glow_opacity * 255 / 100.0;
  1841.   calc.rays_opacity  = gflare->rays_opacity * 255 / 100.0;
  1842.   calc.sflare_opacity = gflare->sflare_opacity * 255 / 100.0;
  1843.  
  1844.   calc.glow_bounds.x0 = calc.xcenter - calc.glow_radius - 0.1;
  1845.   calc.glow_bounds.x1 = calc.xcenter + calc.glow_radius + 0.1;
  1846.   calc.glow_bounds.y0 = calc.ycenter - calc.glow_radius - 0.1;
  1847.   calc.glow_bounds.y1 = calc.ycenter + calc.glow_radius + 0.1;
  1848.   calc.rays_bounds.x0 = calc.xcenter - calc.rays_radius - 0.1;
  1849.   calc.rays_bounds.x1 = calc.xcenter + calc.rays_radius + 0.1;
  1850.   calc.rays_bounds.y0 = calc.ycenter - calc.rays_radius - 0.1;
  1851.   calc.rays_bounds.y1 = calc.ycenter + calc.rays_radius + 0.1;
  1852.  
  1853.   /* Thanks to Marcelo Malheiros for this algorithm */
  1854.   calc.rays_thinness = log (gflare->rays_thickness / 100.0) / log(0.8);
  1855.  
  1856.   calc.rays_spike_mod    = 1.0 / (2 * gflare->rays_nspikes);
  1857.  
  1858.   /*
  1859.     Initialize part of sflare
  1860.     The rest will be initialized in calc_sflare()
  1861.    */
  1862.   calc.sflare_list = NULL;
  1863.   calc.sflare_shape = gflare->sflare_shape;
  1864.   if (calc.sflare_shape == GF_POLYGON)
  1865.     {
  1866.       calc.sflare_angle = 2 * G_PI / (2 * gflare->sflare_nverts);
  1867.       calc.sflare_factor = 1.0 / cos (calc.sflare_angle);
  1868.     }
  1869.  
  1870.   calc.glow_radial     = NULL;
  1871.   calc.glow_angular     = NULL;
  1872.   calc.glow_angular_size = NULL;
  1873.   calc.rays_radial     = NULL;
  1874.   calc.rays_angular     = NULL;
  1875.   calc.rays_angular_size = NULL;
  1876.   calc.sflare_radial     = NULL;
  1877.   calc.sflare_sizefac     = NULL;
  1878.   calc.sflare_probability = NULL;
  1879.  
  1880.   calc.init = TRUE;
  1881. }
  1882.  
  1883. int
  1884. calc_init_progress ()
  1885. {
  1886.   if (calc_sample_one_gradient ())
  1887.     return TRUE;
  1888.   calc_place_sflare ();
  1889.   return FALSE;
  1890. }
  1891.  
  1892. /*
  1893.    Store samples of gradient into an array
  1894.    this routine is called during Calc initialization
  1895.    this code is very messy... :( */
  1896. static int
  1897. calc_sample_one_gradient ()
  1898. {
  1899.   static struct {
  1900.     guchar    **values;
  1901.     gint    name_offset;
  1902.     gint    hue_offset;
  1903.     gint    gray;
  1904.   } table[] = {
  1905.     { &calc.glow_radial, OFFSETOF (GFlare, glow_radial), OFFSETOF (GFlare, glow_hue), FALSE },
  1906.     { &calc.glow_angular, OFFSETOF (GFlare, glow_angular), 0, FALSE },
  1907.     { &calc.glow_angular_size, OFFSETOF (GFlare, glow_angular_size), 0, TRUE },
  1908.     { &calc.rays_radial, OFFSETOF (GFlare, rays_radial), OFFSETOF (GFlare, rays_hue), FALSE },
  1909.     { &calc.rays_angular, OFFSETOF (GFlare, rays_angular), 0, FALSE },
  1910.     { &calc.rays_angular_size, OFFSETOF (GFlare, rays_angular_size), 0, TRUE },
  1911.     { &calc.sflare_radial, OFFSETOF (GFlare, sflare_radial), OFFSETOF (GFlare, sflare_hue), FALSE },
  1912.     { &calc.sflare_sizefac, OFFSETOF (GFlare, sflare_sizefac), 0, TRUE },
  1913.     { &calc.sflare_probability, OFFSETOF (GFlare, sflare_probability), 0, TRUE },
  1914.   };
  1915.   GFlare    *gflare = calc.gflare;
  1916.   GradientName    *grad_name;
  1917.   guchar    *gradient;
  1918.   gdouble    hue_deg;
  1919.   gint        i, j, hue;
  1920.  
  1921.   for (i = 0; i < sizeof (table) / sizeof (table[0]); i++)
  1922.     {
  1923.       if (*(table[i].values) == NULL)
  1924.     {
  1925.       /* @GRADIENT_NAME */
  1926.       grad_name = (GradientName *) ((char*) gflare + table[i].name_offset);
  1927.       gradient = *(table[i].values) = g_new (guchar, 4 * GRADIENT_RESOLUTION);
  1928.       gradient_get_values (*grad_name, gradient, GRADIENT_RESOLUTION);
  1929.  
  1930.       /*
  1931.        * Do hue rotation, if needed
  1932.        */
  1933.  
  1934.       if (table[i].hue_offset != 0)
  1935.         {
  1936.           hue_deg = calc.hue + *(gdouble *) ((char*) gflare + table[i].hue_offset);
  1937.           hue = (gint) (hue_deg / 360.0 * 256.0) % 256;
  1938.           if (hue < 0)
  1939.         hue += 256;
  1940.           g_assert (0 <= hue && hue < 256);
  1941.  
  1942.           if (hue > 0)
  1943.         {
  1944.           for (j = 0; j < GRADIENT_RESOLUTION; j++)
  1945.             {
  1946.               gint    r, g, b;
  1947.  
  1948.               r = gradient[j*4];
  1949.               g = gradient[j*4+1];
  1950.               b = gradient[j*4+2];
  1951.  
  1952.               gimp_rgb_to_hsv (&r, &g, &b);
  1953.               r = (r + hue) % 256;
  1954.               gimp_hsv_to_rgb (&r, &g, &b);
  1955.  
  1956.               gradient[j*4] = r;
  1957.               gradient[j*4+1] = g;
  1958.               gradient[j*4+2] = b;
  1959.             }
  1960.         }
  1961.         }
  1962.  
  1963.       /*
  1964.        *    Grayfy gradient, if needed
  1965.        */
  1966.  
  1967.       if (table[i].gray)
  1968.         {
  1969.           for (j = 0; j < GRADIENT_RESOLUTION; j++)
  1970.         /* the first byte is enough */
  1971.         gradient[j*4] = LUMINOSITY ((gradient + j*4));
  1972.  
  1973.         }
  1974.  
  1975.       /* sampling of one gradient is done */
  1976.       return TRUE;
  1977.     }
  1978.     }
  1979.   return FALSE;
  1980. }
  1981.  
  1982. static void
  1983. calc_place_sflare ()
  1984. {
  1985.   GFlare    *gflare;
  1986.   CalcSFlare    *sflare;
  1987.   gdouble    prob[GRADIENT_RESOLUTION];
  1988.   gdouble    sum, sum2;
  1989.   gdouble    pos;
  1990.   gdouble    rnd, sizefac;
  1991.   int        n;
  1992.   int        i;
  1993.  
  1994.   if ((calc.type & CALC_SFLARE) == 0)
  1995.     return;
  1996.  
  1997.   DEBUG_PRINT (("calc_place_sflare\n"));
  1998.  
  1999.   gflare = calc.gflare;
  2000.  
  2001.   /*
  2002.     Calc cumulative probability
  2003.     */
  2004.  
  2005.   sum = 0.0;
  2006.   for (i = 0; i < GRADIENT_RESOLUTION; i++)
  2007.     {
  2008.       /* probability gradient was grayfied already */
  2009.       prob[i] = calc.sflare_probability[i*4];
  2010.       sum += prob[i];
  2011.     }
  2012.  
  2013.   if (sum == 0.0)
  2014.     sum = 1.0;
  2015.  
  2016.   sum2 = 0;
  2017.   for (i = 0; i < GRADIENT_RESOLUTION; i++)
  2018.     {
  2019.       sum2 += prob[i];        /* cumulation */
  2020.       prob[i] = sum2 / sum;
  2021.     }
  2022.  
  2023.   if (gflare->sflare_seed_time)
  2024.     srand (time (NULL));
  2025.   else
  2026.     srand (gflare->sflare_seed);
  2027.  
  2028.   for (n = 0; n < SFLARE_NUM; n++)
  2029.     {
  2030.       sflare = g_new (CalcSFlare, 1);
  2031.       rnd = (double) rand () / G_MAXRAND;
  2032.       for (i = 0; i < GRADIENT_RESOLUTION; i++)
  2033.     if (prob[i] >= rnd)
  2034.       break;
  2035.       if (i >= GRADIENT_RESOLUTION)
  2036.     i = GRADIENT_RESOLUTION - 1;
  2037.  
  2038.       /* sizefac gradient was grayfied already */
  2039.       sizefac = calc.sflare_sizefac[i*4] / 255.0;
  2040.       sizefac = pow (sizefac, 5.0);
  2041.  
  2042.       pos = (double) (i - GRADIENT_RESOLUTION / 2) / GRADIENT_RESOLUTION;
  2043.       sflare->xcenter = calc.xcenter + cos (calc.vangle) * calc.vlength * pos;
  2044.       sflare->ycenter = calc.ycenter - sin (calc.vangle) * calc.vlength * pos;
  2045.       sflare->radius = sizefac * calc.sflare_radius; /* FIXME */
  2046.       sflare->bounds.x0 = sflare->xcenter - sflare->radius - 1;
  2047.       sflare->bounds.x1 = sflare->xcenter + sflare->radius + 1;
  2048.       sflare->bounds.y0 = sflare->ycenter - sflare->radius - 1;
  2049.       sflare->bounds.y1 = sflare->ycenter + sflare->radius + 1;
  2050.       calc.sflare_list = g_list_append (calc.sflare_list, sflare);
  2051.     }
  2052. }
  2053.  
  2054.  
  2055. void
  2056. calc_deinit ()
  2057. {
  2058.   GList        *list;
  2059.  
  2060.   DEBUG_PRINT (("\\\\\\\\ calc_deinit \\\\\\\\ \n"));
  2061.  
  2062.   if (!calc.init)
  2063.     {
  2064.       g_warning("calc_deinit: not initialized");
  2065.       return;
  2066.     }
  2067.  
  2068.   list = calc.sflare_list;
  2069.   while (list)
  2070.     {
  2071.       g_free (list->data);
  2072.       list = list->next;
  2073.     }
  2074.   g_list_free (calc.sflare_list);
  2075.  
  2076.   g_free (calc.glow_radial);
  2077.   g_free (calc.glow_angular);
  2078.   g_free (calc.glow_angular_size);
  2079.   g_free (calc.rays_radial);
  2080.   g_free (calc.rays_angular);
  2081.   g_free (calc.rays_angular_size);
  2082.   g_free (calc.sflare_radial);
  2083.   g_free (calc.sflare_sizefac);
  2084.   g_free (calc.sflare_probability);
  2085.  
  2086.   calc.init = FALSE;
  2087. }
  2088.  
  2089. /*
  2090.  *  Get sample value at specified position of a gradient
  2091.  *
  2092.  *  gradient samples are stored into array at the time of
  2093.  *  calc_sample_one_gradients (), and it is now linear interpolated.
  2094.  *
  2095.  *  INPUT:
  2096.  *    guchar    gradient[4*GRADIENT_RESOLUTION]        gradient array(RGBA)
  2097.  *    gdouble pos                    position (0<=pos<=1)
  2098.  *  OUTPUT:
  2099.  *    guchar    pix[4]
  2100.  */
  2101. static void
  2102. calc_get_gradient(guchar *pix, guchar *gradient, gdouble pos)
  2103. {
  2104.   gint        ipos;
  2105.   gdouble    frac;
  2106.   gint        i;
  2107.  
  2108.   if (pos < 0 || pos > 1)
  2109.     {
  2110.       pix[0] = pix[1] = pix[2] = pix[3] = 0;
  2111.       return;
  2112.     }
  2113.   pos *= GRADIENT_RESOLUTION - 1.0001;
  2114.   ipos = (gint) pos;    frac = pos - ipos;
  2115.   gradient += ipos * 4;
  2116.  
  2117.   for (i = 0; i < 4; i++)
  2118.     {
  2119.       pix[i] = gradient[i] * (1 - frac) + gradient[i+4] * frac;
  2120.     }
  2121. }
  2122.  
  2123. /* I need fmod to return always positive value */
  2124. static gdouble
  2125. fmod_positive (gdouble x, gdouble m)
  2126. {
  2127.   return x - floor (x/m) * m;
  2128. }
  2129.  
  2130. /*
  2131.  *  Calc glow's pixel (RGBA) value
  2132.  *  INPUT:
  2133.  *    gdouble x, y            image coordinates
  2134.  *  OUTPUT:
  2135.  *    guchar    pix[4]
  2136.  */
  2137. void
  2138. calc_glow_pix (guchar *dest_pix, gdouble x, gdouble y)
  2139. {
  2140.   gdouble radius, angle;
  2141.   gdouble angular_size;
  2142.   guchar  radial_pix[4], angular_pix[4], size_pix[4];
  2143.   gint      i;
  2144.  
  2145.   if ((calc.type & CALC_GLOW) == 0
  2146.       || x < calc.glow_bounds.x0 || x > calc.glow_bounds.x1
  2147.       || y < calc.glow_bounds.y0 || y > calc.glow_bounds.y1)
  2148.     {
  2149.       memset (dest_pix, 0, 4);
  2150.       return;
  2151.     }
  2152.  
  2153.   x -= calc.xcenter;
  2154.   y -= calc.ycenter;
  2155.   radius = sqrt (x*x + y*y) / calc.glow_radius;
  2156.   angle = (atan2 (-y, x) + calc.glow_rotation ) / (2 * G_PI);
  2157.   angle = fmod_positive (angle, 1.0);
  2158.  
  2159.   calc_get_gradient (size_pix, calc.glow_angular_size, angle);
  2160.   /* angular_size gradient was grayfied already */
  2161.   angular_size = size_pix[0] / 255.0;
  2162.   radius /= (angular_size+0.0001);    /* in case angular_size == 0.0 */
  2163.   if(radius < 0 || radius > 1)
  2164.     {
  2165.       memset (dest_pix, 0, 4);
  2166.       return;
  2167.     }
  2168.  
  2169.   calc_get_gradient (radial_pix, calc.glow_radial, radius);
  2170.   calc_get_gradient (angular_pix, calc.glow_angular, angle);
  2171.  
  2172.   for (i = 0; i < 4; i++)
  2173.     dest_pix[i] = radial_pix[i] * angular_pix[i] / 255;
  2174. }
  2175.  
  2176. /*
  2177.  *  Calc rays's pixel (RGBA) value
  2178.  *
  2179.  */
  2180. void
  2181. calc_rays_pix (guchar *dest_pix, gdouble x, gdouble y)
  2182. {
  2183.   gdouble radius, angle;
  2184.   gdouble angular_size;
  2185.   gdouble spike_frac, spike_inten, spike_angle;
  2186.   guchar  radial_pix[4], angular_pix[4], size_pix[4];
  2187.   gint      i;
  2188.  
  2189.   if ((calc.type & CALC_RAYS) == 0
  2190.       || x < calc.rays_bounds.x0 || x > calc.rays_bounds.x1
  2191.       || y < calc.rays_bounds.y0 || y > calc.rays_bounds.y1)
  2192.     {
  2193.       memset (dest_pix, 0, 4);
  2194.       return;
  2195.     }
  2196.  
  2197.   x -= calc.xcenter;
  2198.   y -= calc.ycenter;
  2199.   radius = sqrt (x*x + y*y) / calc.rays_radius;
  2200.   angle = (atan2 (-y, x) + calc.rays_rotation ) / (2 * G_PI);
  2201.   angle = fmod_positive (angle, 1.0);    /* make sure 0 <= angle < 1.0 */
  2202.   spike_frac = fmod (angle, calc.rays_spike_mod * 2);
  2203.   spike_angle = angle - spike_frac + calc.rays_spike_mod;
  2204.   spike_frac = (angle - spike_angle) / calc.rays_spike_mod;
  2205.   /* spike_frac is between -1.0 and 1.0 here (except round error...) */
  2206.  
  2207.   spike_inten = pow (1.0 - fabs (spike_frac), calc.rays_thinness);
  2208.  
  2209.   calc_get_gradient (size_pix, calc.rays_angular_size, spike_angle);
  2210.   /* angular_size gradient was grayfied already */
  2211.   angular_size = size_pix[0] / 255.0;
  2212.   radius /= (angular_size+0.0001);    /* in case angular_size == 0.0 */
  2213.   if(radius < 0 || radius > 1)
  2214.     {
  2215.       memset (dest_pix, 0, 4);
  2216.       return;
  2217.     }
  2218.  
  2219.   calc_get_gradient (radial_pix, calc.rays_radial, radius);
  2220.   calc_get_gradient (angular_pix, calc.rays_angular, spike_angle);
  2221.  
  2222.   for (i = 0; i < 3; i++)
  2223.     dest_pix[i] =  radial_pix[i] * angular_pix[i] / 255;
  2224.   dest_pix[3] = spike_inten * radial_pix[3] * angular_pix[3] / 255;
  2225.  
  2226. }
  2227.  
  2228.  
  2229. /*
  2230.  *  Calc sflare's pixel (RGBA) value
  2231.  *
  2232.  *  the sflare (second flares) are needed to be rendered one each
  2233.  *  sequencially, onto the source image, such as like usual layer
  2234.  *  operations. So the function takes src_pix as argment.  glow, rays
  2235.  *  routines don't have src_pix as argment, because of convienience.
  2236.  *
  2237.  *  @JAPANESE
  2238.  *  sflare $B$OJ#?t$N%U%l%"$r=g$K(B($B%l%$%dE*$K(B)$B$+$V$;$J$,$iIA2h$9$kI,MW$,(B
  2239.  *  $B$"$k$N$G!"$3$l$@$1(B src_pix $B$r0z?t$K$H$C$F(B paint_func $B$rE,MQ$9$k!#(B
  2240.  *  glow, rays $B$O4J0W2=$N$?$a$K$J$7!#(B
  2241.  */
  2242. void
  2243. calc_sflare_pix (guchar *dest_pix, gdouble x, gdouble y, guchar *src_pix)
  2244. {
  2245.   GList        *list;
  2246.   CalcSFlare    *sflare;
  2247.   gdouble    sx, sy, th;
  2248.   gdouble    radius, angle;
  2249.   guchar    radial_pix[4], tmp_pix[4];
  2250.  
  2251.   memcpy (dest_pix, src_pix, 4);
  2252.  
  2253.   if ((calc.type & CALC_SFLARE) == 0)
  2254.     return;
  2255.  
  2256.   list = calc.sflare_list;
  2257.   while (list)
  2258.     {
  2259.       sflare = list->data;
  2260.       list = list->next;
  2261.  
  2262.       if (x < sflare->bounds.x0 || x > sflare->bounds.x1
  2263.       || y < sflare->bounds.y0 || y > sflare->bounds.y1)
  2264.     continue;
  2265.       sx = x - sflare->xcenter;
  2266.       sy = y - sflare->ycenter;
  2267.       radius = sqrt (sx * sx + sy * sy) / sflare->radius;
  2268.       if (calc.sflare_shape == GF_POLYGON)
  2269.     {
  2270.       angle = atan2 (-sy, sx) - calc.vangle + calc.sflare_rotation;
  2271.       th = fmod_positive (angle, calc.sflare_angle * 2) - calc.sflare_angle;
  2272.       radius *= cos (th) * calc.sflare_factor;
  2273.     }
  2274.       if (radius < 0 || radius > 1)
  2275.     continue;
  2276.  
  2277.       calc_get_gradient (radial_pix, calc.sflare_radial, radius);
  2278.       memcpy (tmp_pix, dest_pix, 4);
  2279.       calc_paint_func (dest_pix, tmp_pix, radial_pix,
  2280.                calc.sflare_opacity, calc.gflare->sflare_mode);
  2281.     }
  2282. }
  2283.  
  2284.  
  2285.  
  2286. void
  2287. calc_gflare_pix (guchar *dest_pix, gdouble x, gdouble y, guchar *src_pix)
  2288. {
  2289.   GFlare    *gflare = calc.gflare;
  2290.   guchar    glow_pix[4], rays_pix[4];
  2291.   guchar    tmp_pix[4];
  2292.  
  2293.   memcpy (dest_pix, src_pix, 4);
  2294.  
  2295.   if (calc.type & CALC_GLOW)
  2296.     {
  2297.       memcpy (tmp_pix, dest_pix, 4);
  2298.       calc_glow_pix (glow_pix, x, y);
  2299.       calc_paint_func (dest_pix, tmp_pix, glow_pix,
  2300.                calc.glow_opacity, gflare->glow_mode);
  2301.     }
  2302.   if (calc.type & CALC_RAYS)
  2303.     {
  2304.       memcpy (tmp_pix, dest_pix, 4);
  2305.       calc_rays_pix (rays_pix, x, y);
  2306.       calc_paint_func (dest_pix, tmp_pix, rays_pix,
  2307.                calc.rays_opacity, gflare->rays_mode);
  2308.     }
  2309.   if (calc.type & CALC_SFLARE)
  2310.     {
  2311.       memcpy (tmp_pix, dest_pix, 4);
  2312.       calc_sflare_pix (dest_pix, x, y, tmp_pix);
  2313.     }
  2314. }
  2315.  
  2316. /*
  2317.     Paint func routines, such as Normal, Addition, ...
  2318.  */
  2319. static void
  2320. calc_paint_func (guchar *dest, guchar *src1, guchar *src2, gint opacity,
  2321.          GFlareMode mode)
  2322. {
  2323.   guchar    buf[4], *s=buf;
  2324.  
  2325.   if (src2[3] == 0 || opacity <= 0)
  2326.     {
  2327.       memcpy (dest, src1, 4);
  2328.       return;
  2329.     }
  2330.  
  2331.   switch (mode)
  2332.     {
  2333.     case GF_NORMAL:
  2334.       s = src2;
  2335.       break;
  2336.     case GF_ADDITION:
  2337.       calc_addition (s, src1, src2);
  2338.       break;
  2339.     case GF_OVERLAY:
  2340.       calc_overlay (s, src1, src2);
  2341.       break;
  2342.     case GF_SCREEN:
  2343.       calc_screen (s, src1, src2);
  2344.       break;
  2345.     default:
  2346.       s = src2;
  2347.       break;
  2348.     }
  2349.   calc_combine (dest, src1, s, opacity);
  2350. }
  2351.  
  2352. static void
  2353. calc_combine (guchar *dest, guchar *src1, guchar *src2, gint opacity)
  2354. {
  2355.   gdouble    s1_a, s2_a, new_a;
  2356.   gdouble    ratio, compl_ratio;
  2357.   gint        i;
  2358.  
  2359.   s1_a = src1[3] / 255.0;
  2360.   s2_a = src2[3] * opacity / 65025.0;
  2361.   new_a     = s1_a + (1.0 - s1_a) * s2_a;
  2362.  
  2363.   if (new_a != 0.0)
  2364.     ratio = s2_a / new_a;
  2365.   else
  2366.     ratio = 0.0;
  2367.  
  2368.   compl_ratio = 1.0 - ratio;
  2369.  
  2370.   for (i = 0; i < 3; i++)
  2371.     dest[i] = src1[i] * compl_ratio + src2[i] * ratio;
  2372.  
  2373.   dest[3] = new_a * 255.0;
  2374. }
  2375.  
  2376. static void
  2377. calc_addition (guchar *dest, guchar *src1, guchar *src2)
  2378. {
  2379.   gint        tmp, i;
  2380.  
  2381.   for (i = 0; i < 3; i++)
  2382.     {
  2383.       tmp = src1[i] + src2[i];
  2384.       dest[i] = tmp <= 255 ? tmp: 255;
  2385.     }
  2386.   dest[3] = MIN (src1[3], src2[3]);
  2387. }
  2388.  
  2389. static void
  2390. calc_screen (guchar *dest, guchar *src1, guchar *src2)
  2391. {
  2392.   gint        i;
  2393.  
  2394.   for (i = 0; i < 3; i++)
  2395.     {
  2396.       dest[i] = 255 - ((255 - src1[i]) * (255 - src2[i])) / 255;
  2397.     }
  2398.   dest[3] = MIN (src1[3], src2[3]);
  2399. }
  2400.  
  2401. static void
  2402. calc_overlay (guchar *dest, guchar *src1, guchar *src2)
  2403. {
  2404.   gint        screen, mult, i;
  2405.  
  2406.   for (i = 0; i < 3; i++)
  2407.     {
  2408.       screen = 255 - ((255 - src1[i]) * (255 - src2[i])) / 255;
  2409.       mult = (src1[i] * src2[i]) / 255;
  2410.       dest[i] = (screen * src1[i] + mult * (255 - src1[i])) / 255;
  2411.     }
  2412.   dest[3] = MIN (src1[3], src2[3]);
  2413. }
  2414.  
  2415. /*************************************************************************/
  2416. /**                                    **/
  2417. /**            Main Dialog                    **/
  2418. /**            +++ dlg                        **/
  2419. /**                                    **/
  2420. /*************************************************************************/
  2421.  
  2422. /*
  2423.     This is gflare main dialog, one which opens in first.
  2424.  */
  2425.  
  2426. gint
  2427. dlg_run (void)
  2428. {
  2429.   GtkWidget *shell;
  2430.   GtkWidget *hbox;
  2431.   GtkWidget *frame;
  2432.   GtkWidget *abox;
  2433.   GtkWidget *notebook;
  2434.  
  2435.   gimp_ui_init ("gflare", TRUE);
  2436.  
  2437.   /*
  2438.    *    Init Main Dialog
  2439.    */
  2440.  
  2441.   pint.run = FALSE;
  2442.   dlg = g_new (GFlareDialog, 1);
  2443.   dlg->init = TRUE;
  2444.   dlg->update_preview = TRUE;
  2445.  
  2446.   gradient_menu_init (); /* FIXME: this should go elsewhere  */
  2447.   dlg_setup_gflare ();
  2448.  
  2449.   g_assert (gflares_list != NULL);
  2450.   g_assert (dlg->gflare != NULL);
  2451.   g_assert (dlg->gflare->name != NULL);
  2452.  
  2453.   /*
  2454.    *    Dialog Shell
  2455.    */
  2456.  
  2457.   shell = dlg->shell =
  2458.     gimp_dialog_new (_("GFlare"), "gflare",
  2459.              gimp_standard_help_func, "filters/gflare.html",
  2460.              GTK_WIN_POS_MOUSE,
  2461.              FALSE, TRUE, FALSE,
  2462.  
  2463.              _("OK"), dlg_ok_callback,
  2464.              NULL, NULL, NULL, TRUE, FALSE,
  2465.              _("Cancel"), gtk_widget_destroy,
  2466.              NULL, 1, NULL, FALSE, TRUE,
  2467.  
  2468.              NULL);
  2469.  
  2470.   gtk_signal_connect (GTK_OBJECT (shell), "destroy",
  2471.               GTK_SIGNAL_FUNC (gtk_main_quit),
  2472.               NULL);
  2473.  
  2474.   gimp_help_init ();
  2475.  
  2476.   /*
  2477.    *    main hbox
  2478.    */
  2479.  
  2480.   hbox = gtk_hbox_new (FALSE, 6);
  2481.   gtk_container_set_border_width (GTK_CONTAINER (hbox), 6); 
  2482.   gtk_box_pack_start (GTK_BOX (GTK_DIALOG (shell)->vbox), hbox,
  2483.               FALSE, FALSE, 0);
  2484.   gtk_widget_show (hbox);
  2485.  
  2486.   /*
  2487.    *    Preview
  2488.    */
  2489.  
  2490.   frame = gtk_frame_new (_("Preview")); 
  2491.   gtk_box_pack_start (GTK_BOX (hbox), frame, FALSE, FALSE, 0);
  2492.   gtk_widget_show (frame); 
  2493.  
  2494.   abox = gtk_alignment_new (0.5, 0.5, 0.0, 0.0);
  2495.   gtk_container_set_border_width (GTK_CONTAINER (abox), 4);
  2496.   gtk_container_add (GTK_CONTAINER (frame), abox);
  2497.   gtk_widget_show (abox);
  2498.  
  2499.   frame = gtk_frame_new (NULL); 
  2500.   gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN); 
  2501.   gtk_container_add (GTK_CONTAINER (abox), frame);
  2502.   gtk_widget_show (frame);
  2503.  
  2504.   dlg->preview = preview_new (DLG_PREVIEW_WIDTH, DLG_PREVIEW_HEIGHT,
  2505.                   dlg_preview_init_func, NULL,
  2506.                   dlg_preview_render_func, NULL,
  2507.                   dlg_preview_deinit_func, NULL);
  2508.   gtk_widget_set_events (GTK_WIDGET (dlg->preview->widget), DLG_PREVIEW_MASK);
  2509.   gtk_container_add (GTK_CONTAINER (frame), dlg->preview->widget);
  2510.   gtk_signal_connect (GTK_OBJECT(dlg->preview->widget), "event",
  2511.               GTK_SIGNAL_FUNC (dlg_preview_handle_event),
  2512.               NULL);
  2513.   dlg_preview_calc_window ();
  2514.  
  2515.   /*
  2516.    *    Notebook
  2517.    */
  2518.  
  2519.   notebook = dlg->notebook = gtk_notebook_new ();
  2520.   gtk_notebook_set_tab_pos (GTK_NOTEBOOK (notebook), GTK_POS_TOP);
  2521.   gtk_box_pack_start (GTK_BOX (hbox), notebook, TRUE, TRUE, 0);
  2522.   gtk_widget_show (notebook);
  2523.  
  2524.   dlg_make_page_settings (dlg, notebook);
  2525.   dlg_make_page_selector (dlg, notebook);
  2526.  
  2527.   DEBUG_PRINT (("shell is shown\n"));
  2528.   gtk_widget_show (shell);
  2529.  
  2530.   /*
  2531.    *    Make sure the selector page is realized
  2532.    *    This idea is from app/layers_dialog.c
  2533.    */
  2534.   gtk_notebook_set_page (GTK_NOTEBOOK (notebook), 1);
  2535.   gtk_notebook_set_page (GTK_NOTEBOOK (notebook), 0);
  2536.  
  2537.   /*
  2538.    *    Initialization done
  2539.    */
  2540.   dlg->init = FALSE;
  2541.   dlg_preview_update ();
  2542.  
  2543.   DEBUG_PRINT (("dlg init done\n"));
  2544.  
  2545.   gtk_main ();
  2546.   gimp_help_free ();
  2547.   gdk_flush ();
  2548.  
  2549.   return pint.run;
  2550. }
  2551.  
  2552. static void
  2553. dlg_ok_callback (GtkWidget *widget,
  2554.          gpointer   data)
  2555. {
  2556.   /* @GFLARE_NAME */
  2557.   gflare_name_copy (pvals.gflare_name, dlg->gflare->name);
  2558.  
  2559.   pint.run = TRUE;
  2560.   gtk_widget_destroy (GTK_WIDGET (data));
  2561. }
  2562.  
  2563. static void
  2564. dlg_setup_gflare (void)
  2565. {
  2566.   dlg->gflare = gflares_list_lookup (pvals.gflare_name);
  2567.  
  2568.   if (dlg->gflare == NULL)
  2569.     {
  2570.       dlg->gflare = gflares_list_lookup ("Default");
  2571.       if (dlg->gflare == NULL)
  2572.     {
  2573.       g_warning (_("`Default' is created."));
  2574.       dlg->gflare = gflare_new_with_default (_("Default"));
  2575.       gflares_list_insert (dlg->gflare);
  2576.     }
  2577.     }
  2578. }
  2579.  
  2580. static void
  2581. dlg_page_map_callback (GtkWidget *widget, gpointer data)
  2582. {
  2583.   /* dlg_preview_update (); */
  2584. }
  2585.  
  2586. /***********************************/
  2587. /**    Main Dialog / Preview      **/
  2588. /***********************************/
  2589.  
  2590. /*
  2591.  *    Calculate preview's window, ie. translation of preview widget and
  2592.  *    drawable.
  2593.  *
  2594.  *    x0, x1, y0, y1 are drawable coord, corresponding with top left
  2595.  *    corner of preview widget, etc.
  2596.  */
  2597. void
  2598. dlg_preview_calc_window (void)
  2599. {
  2600.   gint     is_wide;
  2601.   gdouble  offx, offy;
  2602.  
  2603.   is_wide = ((double) DLG_PREVIEW_HEIGHT * drawable->width
  2604.          >= (double) DLG_PREVIEW_WIDTH * drawable->height);
  2605.   if (is_wide)
  2606.     {
  2607.       offy = ((double) drawable->width * DLG_PREVIEW_HEIGHT / DLG_PREVIEW_WIDTH) / 2.0;
  2608.  
  2609.       dlg->pwin.x0 = 0;
  2610.       dlg->pwin.x1 = drawable->width;
  2611.       dlg->pwin.y0 = drawable->height / 2.0 - offy;
  2612.       dlg->pwin.y1 = drawable->height / 2.0 + offy;
  2613.     }
  2614.   else
  2615.     {
  2616.       offx = ((double) drawable->height * DLG_PREVIEW_WIDTH / DLG_PREVIEW_HEIGHT) / 2.0;
  2617.  
  2618.       dlg->pwin.x0 = drawable->width / 2.0 - offx;
  2619.       dlg->pwin.x1 = drawable->width / 2.0 + offx;
  2620.       dlg->pwin.y0 = 0;
  2621.       dlg->pwin.y1 = drawable->height;
  2622.     }
  2623. }
  2624.  
  2625. void
  2626. ed_preview_calc_window (void)
  2627. {
  2628.   gint     is_wide;
  2629.   gdouble  offx, offy;
  2630.  
  2631.   is_wide = ((double) DLG_PREVIEW_HEIGHT * drawable->width
  2632.          >= (double) DLG_PREVIEW_WIDTH * drawable->height);
  2633.   if (is_wide)
  2634.     {
  2635.       offy = ((double) drawable->width * DLG_PREVIEW_HEIGHT / DLG_PREVIEW_WIDTH) / 2.0;
  2636.  
  2637.       dlg->pwin.x0 = 0;
  2638.       dlg->pwin.x1 = drawable->width;
  2639.       dlg->pwin.y0 = drawable->height / 2.0 - offy;
  2640.       dlg->pwin.y1 = drawable->height / 2.0 + offy;
  2641.     }
  2642.   else
  2643.     {
  2644.       offx = ((double) drawable->height * DLG_PREVIEW_WIDTH / DLG_PREVIEW_HEIGHT) / 2.0;
  2645.  
  2646.       dlg->pwin.x0 = drawable->width / 2.0 - offx;
  2647.       dlg->pwin.x1 = drawable->width / 2.0 + offx;
  2648.       dlg->pwin.y0 = 0;
  2649.       dlg->pwin.y1 = drawable->height;
  2650.     }
  2651. }
  2652.  
  2653. gint
  2654. dlg_preview_handle_event (GtkWidget *widget, GdkEvent *event)
  2655. {
  2656.   GdkEventButton *bevent;
  2657.   gint         bx, by, x, y;
  2658.  
  2659.   switch (event->type)
  2660.     {
  2661.     case GDK_BUTTON_PRESS:
  2662.       bevent = (GdkEventButton *) event;
  2663.       bx = bevent->x;
  2664.       by = bevent->y;
  2665.  
  2666.       /* convert widget coord to drawable coord */
  2667.       x = dlg->pwin.x0 + (double) (dlg->pwin.x1 - dlg->pwin.x0)
  2668.                     * bx / DLG_PREVIEW_WIDTH;
  2669.       y = dlg->pwin.y0 + (double) (dlg->pwin.y1 - dlg->pwin.y0)
  2670.                     * by / DLG_PREVIEW_HEIGHT;
  2671.       DEBUG_PRINT (("dlg_preview_handle_event: bxy [%d,%d] xy [%d,%d]\n",
  2672.             bx, by, x, y));
  2673.  
  2674.       if ((x != pvals.xcenter || y != pvals.ycenter))
  2675.     {
  2676.       if (x != pvals.xcenter)
  2677.         {
  2678.           pvals.xcenter = x;
  2679.           gimp_size_entry_set_refval (GIMP_SIZE_ENTRY (dlg->sizeentry),
  2680.                       0, x);
  2681.         }
  2682.       if (y != pvals.ycenter)
  2683.         {
  2684.           pvals.ycenter = y;
  2685.           gimp_size_entry_set_refval (GIMP_SIZE_ENTRY (dlg->sizeentry),
  2686.                       1, y);
  2687.         }
  2688.       dlg_preview_update ();
  2689.     }
  2690.       return TRUE;
  2691.     default:
  2692.       break;
  2693.     }
  2694.   return FALSE;
  2695. }
  2696.  
  2697. gint
  2698. ed_preview_handle_event (GtkWidget *widget,
  2699.              GdkEvent  *event)
  2700. {
  2701.   GdkEventButton *bevent;
  2702.   gint         bx, by, x, y;
  2703.  
  2704.   switch (event->type)
  2705.     {
  2706.     case GDK_BUTTON_PRESS:
  2707.       bevent = (GdkEventButton *) event;
  2708.       bx = bevent->x;
  2709.       by = bevent->y;
  2710.  
  2711.       /* convert widget coord to drawable coord */
  2712.       x = dlg->pwin.x0 + (double) (dlg->pwin.x1 - dlg->pwin.x0)
  2713.                     * bx / DLG_PREVIEW_WIDTH;
  2714.       y = dlg->pwin.y0 + (double) (dlg->pwin.y1 - dlg->pwin.y0)
  2715.                     * by / DLG_PREVIEW_HEIGHT;
  2716.       DEBUG_PRINT (("dlg_preview_handle_event: bxy [%d,%d] xy [%d,%d]\n",
  2717.             bx, by, x, y));
  2718.  
  2719.       if ((x != pvals.xcenter || y != pvals.ycenter))
  2720.     {
  2721.       if (x != pvals.xcenter)
  2722.         {
  2723.           pvals.xcenter = x;
  2724.           gimp_size_entry_set_refval (GIMP_SIZE_ENTRY (dlg->sizeentry),
  2725.                       0, x);
  2726.         }
  2727.       if (y != pvals.ycenter)
  2728.         {
  2729.           pvals.ycenter = y;
  2730.           gimp_size_entry_set_refval (GIMP_SIZE_ENTRY (dlg->sizeentry),
  2731.                       1, y);
  2732.         }
  2733.       dlg_preview_update ();
  2734.     }
  2735.       return TRUE;
  2736.     default:
  2737.       break;
  2738.     }
  2739.   return FALSE;
  2740. }
  2741.  
  2742. static void
  2743. dlg_preview_update ()
  2744. {
  2745.   if (dlg->init)
  2746.     return;
  2747.  
  2748.   if (dlg->update_preview)
  2749.     {
  2750.       dlg->init_params_done = FALSE;
  2751.       preview_render_start (dlg->preview);
  2752.     }
  2753. }
  2754.  
  2755. /*    preview callbacks    */
  2756. static gint
  2757. dlg_preview_init_func (Preview *preview, gpointer data)
  2758. {
  2759.   /* call init_params first, and iterate init_progress while
  2760.      it returns true */
  2761.   if (dlg->init_params_done == FALSE)
  2762.     {
  2763.       calc_init_params (dlg->gflare,
  2764.             CALC_GLOW | CALC_RAYS | CALC_SFLARE,
  2765.             pvals.xcenter, pvals.ycenter,
  2766.             pvals.radius, pvals.rotation, pvals.hue,
  2767.             pvals.vangle, pvals.vlength);
  2768.       dlg->init_params_done = TRUE;
  2769.       return TRUE;
  2770.     }
  2771.   return calc_init_progress ();
  2772. }
  2773.  
  2774. /* render preview
  2775.    do what "preview" means, ie. render lense flare effect onto drawable */
  2776. static void
  2777. dlg_preview_render_func (Preview  *preview,
  2778.              guchar   *dest,
  2779.              gint      y,
  2780.              gpointer  data)
  2781. {
  2782.   GimpPixelRgn  srcPR;
  2783.   gint          x;
  2784.   gint          dx, dy;        /* drawable x, y */
  2785.   guchar       *src_row, *src;
  2786.   guchar        src_pix[4], dest_pix[4];
  2787.   gint          b;
  2788.  
  2789.   dy = dlg->pwin.y0 + (gdouble) (dlg->pwin.y1 - dlg->pwin.y0) * y / DLG_PREVIEW_HEIGHT;
  2790.   if (dy < 0 || dy >= drawable->height)
  2791.     {
  2792.       memset (dest, GRAY50, 3 * DLG_PREVIEW_WIDTH);
  2793.       return;
  2794.     }
  2795.  
  2796.   src_row = g_new (guchar, drawable->bpp * drawable->width);
  2797.   gimp_pixel_rgn_init (&srcPR, drawable,
  2798.                0, 0, drawable->width, drawable->height, FALSE, FALSE);
  2799.   gimp_pixel_rgn_get_row (&srcPR, src_row, 0, dy, drawable->width);
  2800.  
  2801.   for (x = 0; x < DLG_PREVIEW_HEIGHT; x++)
  2802.     {
  2803.       dx = dlg->pwin.x0 + (double) (dlg->pwin.x1 - dlg->pwin.x0) * x / DLG_PREVIEW_WIDTH;
  2804.       if (dx < 0 || dx >= drawable->width)
  2805.     {
  2806.       for (b = 0; b < 3; b++)
  2807.         *dest++ = GRAY50;
  2808.       continue;
  2809.     }
  2810.  
  2811.       /* Get drawable pix value */
  2812.       src = &src_row[dx * drawable->bpp];
  2813.  
  2814.       for (b = 0; b < 3; b++)
  2815.     src_pix[b] = dinfo.is_color ? src[b] : src[0];
  2816.       src_pix[3] = dinfo.has_alpha ? src[drawable->bpp-1] : OPAQUE;
  2817.  
  2818.       /* Get GFlare pix value */
  2819.  
  2820.       calc_gflare_pix (dest_pix, dx, dy, src_pix);
  2821.  
  2822.       /* Draw gray check if needed */
  2823.       preview_rgba_to_rgb (dest, x, y, dest_pix);
  2824.       dest += 3;
  2825.     }
  2826.  
  2827.   g_free (src_row);
  2828. }
  2829.  
  2830. static void
  2831. dlg_preview_deinit_func (Preview *preview, gpointer data)
  2832. {
  2833.   if (dlg->init_params_done)
  2834.     {
  2835.       calc_deinit ();
  2836.       dlg->init_params_done = TRUE;
  2837.     }
  2838. }
  2839.  
  2840. /*****************************************/
  2841. /**    Main Dialog / Settings Page    **/
  2842. /*****************************************/
  2843.  
  2844. static void
  2845. dlg_make_page_settings (GFlareDialog *dlg,
  2846.             GtkWidget    *notebook)
  2847. {
  2848.   GtkWidget *main_vbox;
  2849.   GtkWidget *frame;
  2850.   GtkWidget *center;
  2851.   GtkWidget *chain;
  2852.   GtkWidget *table;
  2853.   GtkWidget *button;
  2854.   GtkWidget *vbox;
  2855.   GtkWidget *asup_table;
  2856.   GtkWidget *scale;
  2857.   GtkObject *adj;
  2858.   gdouble    xres, yres;
  2859.   gint       row;
  2860.  
  2861.   main_vbox = gtk_vbox_new (FALSE, 4);
  2862.   gtk_container_set_border_width (GTK_CONTAINER (main_vbox), 4);
  2863.  
  2864.   frame = gtk_frame_new (_("Center"));
  2865.   gtk_box_pack_start (GTK_BOX (main_vbox), frame, FALSE, FALSE, 0);
  2866.   gtk_widget_show (frame);
  2867.  
  2868.   gimp_image_get_resolution (image_ID, &xres, &yres);
  2869.  
  2870.   center = dlg->sizeentry =
  2871.     gimp_coordinates_new (gimp_image_get_unit (image_ID), "%a",
  2872.               TRUE, TRUE, 75, GIMP_SIZE_ENTRY_UPDATE_SIZE,
  2873.  
  2874.               FALSE, FALSE,
  2875.  
  2876.               _("X:"), pvals.xcenter, xres,
  2877.               -GIMP_MAX_IMAGE_SIZE, GIMP_MAX_IMAGE_SIZE,
  2878.               0, gimp_drawable_width (drawable->id),
  2879.  
  2880.               _("Y:"), pvals.ycenter, yres,
  2881.               -GIMP_MAX_IMAGE_SIZE, GIMP_MAX_IMAGE_SIZE,
  2882.               0, gimp_drawable_height (drawable->id));
  2883.  
  2884.   chain = GTK_WIDGET (GIMP_COORDINATES_CHAINBUTTON (center));
  2885.  
  2886.   gtk_container_set_border_width (GTK_CONTAINER (center), 4);
  2887.   gtk_container_add (GTK_CONTAINER (frame), center);
  2888.   gtk_signal_connect (GTK_OBJECT (center), "value_changed",
  2889.               GTK_SIGNAL_FUNC (dlg_position_entry_callback),
  2890.               NULL);
  2891.   gtk_signal_connect (GTK_OBJECT (center), "refval_changed",
  2892.               GTK_SIGNAL_FUNC (dlg_position_entry_callback),
  2893.               NULL);
  2894.   gtk_widget_hide (chain);
  2895.   gtk_widget_show (center);
  2896.  
  2897.   frame = gtk_frame_new (_("Parameters"));
  2898.   gtk_box_pack_start (GTK_BOX (main_vbox), frame, FALSE, FALSE, 0);
  2899.   gtk_widget_show (frame);
  2900.  
  2901.   table = gtk_table_new (5, 3, FALSE);
  2902.   gtk_container_set_border_width (GTK_CONTAINER (table), 4);
  2903.   gtk_table_set_row_spacings (GTK_TABLE (table), 2);
  2904.   gtk_table_set_col_spacings (GTK_TABLE (table), 4);
  2905.   gtk_container_add (GTK_CONTAINER (frame), table);
  2906.   gtk_widget_show (table);
  2907.  
  2908.   row = 0;
  2909.  
  2910.   adj = gimp_scale_entry_new (GTK_TABLE (table), 0, row++,
  2911.                   _("Radius:"), SCALE_WIDTH, 0,
  2912.                   pvals.radius, 0.0, drawable->width / 2,
  2913.                   1.0, 10.0, 1,
  2914.                   FALSE, 0.0, GIMP_MAX_IMAGE_SIZE,
  2915.                   NULL, NULL);
  2916.   gtk_signal_connect (GTK_OBJECT (adj), "value_changed",
  2917.               GTK_SIGNAL_FUNC (gimp_double_adjustment_update),
  2918.               &pvals.radius);
  2919.   gtk_signal_connect (GTK_OBJECT (adj), "value_changed",
  2920.               GTK_SIGNAL_FUNC (dlg_preview_update),
  2921.               NULL);
  2922.  
  2923.   adj = gimp_scale_entry_new (GTK_TABLE (table), 0, row++,
  2924.                   _("Rotation:"), SCALE_WIDTH, 0,
  2925.                   pvals.rotation, -180.0, 180.0, 1.0, 15.0, 1,
  2926.                   TRUE, 0, 0,
  2927.                   NULL, NULL);
  2928.   gtk_signal_connect (GTK_OBJECT (adj), "value_changed",
  2929.               GTK_SIGNAL_FUNC (gimp_double_adjustment_update),
  2930.               &pvals.rotation);
  2931.   gtk_signal_connect (GTK_OBJECT (adj), "value_changed",
  2932.               GTK_SIGNAL_FUNC (dlg_preview_update),
  2933.               NULL);
  2934.  
  2935.   adj = gimp_scale_entry_new (GTK_TABLE (table), 0, row++,
  2936.                   _("Hue Rotation:"), SCALE_WIDTH, 0,
  2937.                   pvals.hue, -180.0, 180.0, 1.0, 15.0, 1,
  2938.                   TRUE, 0, 0,
  2939.                   NULL, NULL);
  2940.   gtk_signal_connect (GTK_OBJECT (adj), "value_changed",
  2941.               GTK_SIGNAL_FUNC (gimp_double_adjustment_update),
  2942.               &pvals.hue);
  2943.   gtk_signal_connect (GTK_OBJECT (adj), "value_changed",
  2944.               GTK_SIGNAL_FUNC (dlg_preview_update),
  2945.               NULL);
  2946.  
  2947.   adj = gimp_scale_entry_new (GTK_TABLE (table), 0, row++,
  2948.                   _("Vector Angle:"), SCALE_WIDTH, 0,
  2949.                   pvals.vangle, 0.0, 359.0, 1.0, 15.0, 1,
  2950.                   TRUE, 0, 0,
  2951.                   NULL, NULL);
  2952.   gtk_signal_connect (GTK_OBJECT (adj), "value_changed",
  2953.               GTK_SIGNAL_FUNC (gimp_double_adjustment_update),
  2954.               &pvals.vangle);
  2955.   gtk_signal_connect (GTK_OBJECT (adj), "value_changed",
  2956.               GTK_SIGNAL_FUNC (dlg_preview_update),
  2957.               NULL);
  2958.  
  2959.   adj = gimp_scale_entry_new (GTK_TABLE (table), 0, row++,
  2960.                   _("Vector Length:"), SCALE_WIDTH, 0,
  2961.                   pvals.vlength, 1, 1000, 1.0, 10.0, 1,
  2962.                   FALSE, 1, GIMP_MAX_IMAGE_SIZE,
  2963.                   NULL, NULL);
  2964.   gtk_signal_connect (GTK_OBJECT (adj), "value_changed",
  2965.               GTK_SIGNAL_FUNC (gimp_double_adjustment_update),
  2966.               &pvals.vlength);
  2967.   gtk_signal_connect (GTK_OBJECT (adj), "value_changed",
  2968.               GTK_SIGNAL_FUNC (dlg_preview_update),
  2969.               NULL);
  2970.  
  2971.   /**
  2972.   ***    Asupsample settings
  2973.   ***    This code is stolen from gimp-0.99.x/app/blend.c
  2974.   **/
  2975.  
  2976.   /*  asupsample frame */
  2977.   frame = dlg->asupsample_frame = gtk_frame_new (NULL);
  2978.   gtk_box_pack_start (GTK_BOX (main_vbox), frame, FALSE, FALSE, 0);
  2979.   gtk_widget_show (frame);
  2980.  
  2981.   vbox = gtk_vbox_new (FALSE, 2);
  2982.   gtk_container_set_border_width (GTK_CONTAINER (vbox), 4);
  2983.   gtk_container_add (GTK_CONTAINER (frame), vbox);
  2984.   gtk_widget_show (vbox);
  2985.  
  2986.   button = gtk_check_button_new_with_label (_("Adaptive Supersampling"));
  2987.   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button),
  2988.                 pvals.use_asupsample);
  2989.   gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0);
  2990.   gtk_widget_show (button);
  2991.  
  2992.   asup_table = gtk_table_new (2, 2, FALSE);
  2993.   gtk_table_set_col_spacings (GTK_TABLE (asup_table), 4);
  2994.   gtk_table_set_row_spacings (GTK_TABLE (asup_table), 2);
  2995.   gtk_box_pack_start (GTK_BOX (vbox), asup_table, FALSE, FALSE, 0);
  2996.  
  2997.   gtk_widget_set_sensitive (asup_table, pvals.use_asupsample);
  2998.   gtk_object_set_data (GTK_OBJECT (button), "set_sensitive", asup_table);
  2999.   gtk_signal_connect (GTK_OBJECT (button), "toggled",
  3000.               GTK_SIGNAL_FUNC (gimp_toggle_button_update),
  3001.               &pvals.use_asupsample);
  3002.  
  3003.   adj = gtk_adjustment_new (pvals.asupsample_max_depth,
  3004.                 1.0, 10.0, 1.0, 1.0, 1.0);
  3005.   scale = gtk_hscale_new (GTK_ADJUSTMENT (adj));
  3006.   gtk_scale_set_digits (GTK_SCALE (scale), 0);
  3007.   gtk_scale_set_value_pos (GTK_SCALE (scale), GTK_POS_TOP);
  3008.   gtk_signal_connect (GTK_OBJECT (adj), "value_changed",
  3009.               GTK_SIGNAL_FUNC (gimp_int_adjustment_update),
  3010.               &pvals.asupsample_max_depth);
  3011.   gimp_table_attach_aligned (GTK_TABLE (asup_table), 0, 0,
  3012.                  _("Max Depth:"), 1.0, 1.0,
  3013.                  scale, 1, FALSE);
  3014.  
  3015.   adj = gtk_adjustment_new (pvals.asupsample_threshold,
  3016.                 0.0, 4.0, 0.01, 0.01, 0.0);
  3017.   scale = gtk_hscale_new (GTK_ADJUSTMENT (adj));
  3018.   gtk_scale_set_digits (GTK_SCALE (scale), 2);
  3019.   gtk_scale_set_value_pos (GTK_SCALE (scale), GTK_POS_TOP);
  3020.   gtk_signal_connect (GTK_OBJECT (adj), "value_changed",
  3021.               GTK_SIGNAL_FUNC (gimp_double_adjustment_update),
  3022.               &pvals.asupsample_threshold);
  3023.   gimp_table_attach_aligned (GTK_TABLE (asup_table), 0, 1,
  3024.                  _("Threshold:"), 1.0, 1.0,
  3025.                  scale, 1, FALSE);
  3026.  
  3027.   gtk_widget_show (asup_table);
  3028.  
  3029.   button = gtk_check_button_new_with_label (_("Auto Update Preview"));
  3030.   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), dlg->update_preview);
  3031.   gtk_signal_connect (GTK_OBJECT (button), "toggled",
  3032.               GTK_SIGNAL_FUNC (dlg_update_preview_callback),
  3033.               &dlg->update_preview);
  3034.   gtk_box_pack_start (GTK_BOX (main_vbox), button, FALSE, FALSE, 0);
  3035.   gtk_widget_show (button);
  3036.  
  3037.   /*
  3038.    *    Create Page
  3039.    */
  3040.   gtk_notebook_append_page (GTK_NOTEBOOK (notebook), main_vbox,
  3041.                 gtk_label_new (_("Settings")));
  3042.   gtk_signal_connect (GTK_OBJECT (table), "map",
  3043.               GTK_SIGNAL_FUNC (dlg_page_map_callback),
  3044.               (gpointer) PAGE_SETTINGS);
  3045.   gtk_widget_show (main_vbox);
  3046. }
  3047.  
  3048.  
  3049. static void
  3050. dlg_position_entry_callback (GtkWidget *widget, gpointer data)
  3051. {
  3052.   gint x, y;
  3053.  
  3054.   x = RINT (gimp_size_entry_get_refval (GIMP_SIZE_ENTRY (widget), 0));
  3055.   y = RINT (gimp_size_entry_get_refval (GIMP_SIZE_ENTRY (widget), 1));
  3056.  
  3057.   DEBUG_PRINT (("dlg_position_entry_callback\n"));
  3058.  
  3059.   if (pvals.xcenter != x ||
  3060.       pvals.ycenter != y)
  3061.     {
  3062.       pvals.xcenter = x;
  3063.       pvals.ycenter = y;
  3064.  
  3065.       dlg_preview_update ();
  3066.     }
  3067. }
  3068.  
  3069. static void
  3070. dlg_update_preview_callback (GtkWidget *widget,
  3071.                  gpointer   data)
  3072. {
  3073.   gimp_toggle_button_update (widget, data);
  3074.  
  3075.   dlg_preview_update ();
  3076. }
  3077.  
  3078. /*****************************************/
  3079. /**    Main Dialog / Selector Page    **/
  3080. /*****************************************/
  3081.  
  3082. static void
  3083. dlg_make_page_selector (GFlareDialog *dlg,
  3084.             GtkWidget    *notebook)
  3085. {
  3086.   GtkWidget *vbox;
  3087.   GtkWidget *hbox;
  3088.   GtkWidget *listbox;
  3089.   GtkWidget *list;
  3090.   GtkWidget *button;
  3091.   gint       i;
  3092.   static struct
  3093.   {
  3094.     gchar         *label;
  3095.     GtkSignalFunc  callback;
  3096.   }
  3097.   buttons[] =
  3098.   {
  3099.     { N_("New"),    (GtkSignalFunc) &dlg_selector_new_callback },
  3100.     { N_("Edit"),   (GtkSignalFunc) &dlg_selector_edit_callback },
  3101.     { N_("Copy"),   (GtkSignalFunc) &dlg_selector_copy_callback },
  3102.     { N_("Delete"), (GtkSignalFunc) &dlg_selector_delete_callback }
  3103.   };
  3104.  
  3105.   DEBUG_PRINT (("dlg_make_page_selector\n"));
  3106.  
  3107.   vbox = gtk_vbox_new (FALSE, 4);
  3108.   gtk_container_set_border_width (GTK_CONTAINER (vbox), 4);
  3109.  
  3110.   /*
  3111.    *    List Box
  3112.    */
  3113.  
  3114.   listbox = gtk_scrolled_window_new (NULL, NULL);
  3115.   gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (listbox),
  3116.                   GTK_POLICY_AUTOMATIC,
  3117.                   GTK_POLICY_AUTOMATIC);
  3118.  
  3119.   gtk_widget_set_usize (listbox, DLG_LISTBOX_WIDTH, DLG_LISTBOX_HEIGHT);
  3120.   gtk_box_pack_start (GTK_BOX (vbox), listbox, TRUE, TRUE, 0);
  3121.  
  3122.   list = dlg->selector_list = gtk_list_new ();
  3123.   gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW (listbox), list);
  3124.   gtk_widget_show (listbox);
  3125.   gtk_list_set_selection_mode (GTK_LIST (list), GTK_SELECTION_BROWSE);
  3126.   gtk_widget_show (list);
  3127.  
  3128.   dlg_selector_setup_listbox ();
  3129.  
  3130.   /*
  3131.    *    The buttons for the possible listbox operations
  3132.    */
  3133.  
  3134.   hbox = gtk_hbox_new (FALSE, 4);
  3135.   for (i = 0; i < sizeof (buttons) / sizeof (buttons[0]); i++)
  3136.     {
  3137.       button = gtk_button_new_with_label (gettext (buttons[i].label));
  3138.       gtk_signal_connect (GTK_OBJECT (button), "clicked",
  3139.               buttons[i].callback,
  3140.               button);
  3141.       gtk_box_pack_start (GTK_BOX (hbox), button, TRUE, TRUE, 0);
  3142.       gtk_widget_show (button);
  3143.     }
  3144.   gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
  3145.   gtk_widget_show (hbox);
  3146.  
  3147.   gtk_widget_show (vbox);
  3148.  
  3149.   /*
  3150.    *    Create Page
  3151.    */
  3152.   gtk_notebook_append_page (GTK_NOTEBOOK (notebook), vbox,
  3153.                 gtk_label_new (_("Selector")));
  3154.   gtk_signal_connect (GTK_OBJECT (vbox), "map",
  3155.               GTK_SIGNAL_FUNC (dlg_page_map_callback),
  3156.               (gpointer) PAGE_SELECTOR);
  3157.   gtk_widget_show (vbox);
  3158. }
  3159.  
  3160.  
  3161. /*
  3162.  *    Set up selector's listbox, according to gflares_list
  3163.  */
  3164. static void
  3165. dlg_selector_setup_listbox (void)
  3166. {
  3167.   GList  *list;
  3168.   GFlare *gflare;
  3169.   gint    n;
  3170.  
  3171.   DEBUG_PRINT (("dlg_selector_setup_listbox\n"));
  3172.  
  3173.   list = gflares_list;
  3174.   n = 0;
  3175.  
  3176.   while (list)
  3177.     {
  3178.       gflare = list->data;
  3179.  
  3180.       /*
  3181.     dlg->gflare should be valid (ie. not NULL) here.
  3182.     */
  3183.       if (gflare == dlg->gflare)
  3184.     dlg_selector_insert (gflare, n, 1);
  3185.       else
  3186.     dlg_selector_insert (gflare, n, 0);
  3187.  
  3188.       list = list->next;
  3189.       n++;
  3190.     }
  3191. }
  3192.  
  3193. /*
  3194.  *    Insert new list_item to selector's listbox
  3195.  */
  3196. static void
  3197. dlg_selector_insert (GFlare *gflare,
  3198.              gint    pos,
  3199.              gint    select)
  3200. {
  3201.   GtkWidget *list_item;
  3202.   GList     *list;
  3203.  
  3204.   DEBUG_PRINT (("dlg_selector_insert %s %d\n", gflare->name, pos));
  3205.  
  3206.   list_item = gtk_list_item_new_with_label (gflare->name);
  3207.   /* gflare->list_item = list_item; */
  3208.   gtk_signal_connect (GTK_OBJECT (list_item), "select",
  3209.               GTK_SIGNAL_FUNC (dlg_selector_list_item_callback),
  3210.               (gpointer) gflare);
  3211.   gtk_widget_show (list_item);
  3212.  
  3213.   list = g_list_append (NULL, list_item);
  3214.   gtk_list_insert_items (GTK_LIST (dlg->selector_list), list, pos);
  3215.  
  3216.   if (select)
  3217.     gtk_list_select_item (GTK_LIST (dlg->selector_list), pos);
  3218. }
  3219.  
  3220. static void
  3221. dlg_selector_list_item_callback (GtkWidget *widget,
  3222.                  gpointer   data)
  3223. {
  3224.   if (widget->state != GTK_STATE_SELECTED)
  3225.     return;
  3226.  
  3227.   dlg->gflare = data;
  3228.  
  3229.   dlg_preview_update ();
  3230. }
  3231.  
  3232.  
  3233. /*
  3234.  *    "New" button in Selector page
  3235.  */
  3236. static void
  3237. dlg_selector_new_callback (GtkWidget *widget,
  3238.                gpointer data)
  3239. {
  3240.   GtkWidget *query_box;
  3241.  
  3242.   query_box = gimp_query_string_box (_("New GFlare"),
  3243.                      gimp_standard_help_func,
  3244.                      "filters/gflare.html",
  3245.                      _("Enter a Name for the New GFlare:"),
  3246.                      _("untitled"),
  3247.                      NULL, NULL,
  3248.                      dlg_selector_new_ok_callback, dlg);
  3249.   gtk_widget_show (query_box);
  3250. }
  3251.  
  3252. static void
  3253. dlg_selector_new_ok_callback (GtkWidget *widget,
  3254.                   gchar     *new_name,
  3255.                   gpointer   data)
  3256. {
  3257.   GFlare *gflare;
  3258.   gint    pos;
  3259.  
  3260.   g_assert (new_name != NULL);
  3261.  
  3262.   if (gflares_list_lookup (new_name))
  3263.     {
  3264.       g_message (_("The name '%s' is used already!"), new_name);
  3265.       return;
  3266.     }
  3267.  
  3268.   gflare = gflare_new_with_default (new_name);
  3269.  
  3270.   pos = gflares_list_insert (gflare);
  3271.   dlg_selector_insert (gflare, pos, 1);
  3272.  
  3273.   dlg->gflare = gflare;
  3274.   dlg_preview_update ();
  3275. }
  3276.  
  3277. /*
  3278.  *  "Edit" button in Selector page
  3279.  */
  3280. static void
  3281. dlg_selector_edit_callback (GtkWidget *widget,
  3282.                 gpointer   data)
  3283. {
  3284.   preview_render_end (dlg->preview);
  3285.   gtk_widget_set_sensitive (dlg->shell, FALSE);
  3286.   ed_run (dlg->gflare, dlg_selector_edit_done_callback, NULL);
  3287. }
  3288.  
  3289. static void
  3290. dlg_selector_edit_done_callback (gint     updated,
  3291.                  gpointer data)
  3292. {
  3293.   gtk_widget_set_sensitive (dlg->shell, TRUE);
  3294.   if (updated)
  3295.     {
  3296.       gflare_save (dlg->gflare);
  3297.     }
  3298.   dlg_preview_update ();
  3299. }
  3300.  
  3301. /*
  3302.  *  "Copy" button in Selector page
  3303.  */
  3304. static void
  3305. dlg_selector_copy_callback (GtkWidget *widget,
  3306.                 gpointer   data)
  3307. {
  3308.   GtkWidget *query_box;
  3309.   gchar     *name;
  3310.  
  3311.   name = g_strdup_printf ("%s copy", dlg->gflare->name);
  3312.  
  3313.   query_box = gimp_query_string_box (_("Copy GFlare"),
  3314.                      gimp_standard_help_func,
  3315.                      "filters/gflare.html",
  3316.                      _("Enter a Name for the Copied GFlare:"),
  3317.                      name,
  3318.                      NULL, NULL,
  3319.                      dlg_selector_copy_ok_callback, dlg);
  3320.   g_free (name);
  3321.  
  3322.   gtk_widget_show (query_box);
  3323. }
  3324.  
  3325. static void
  3326. dlg_selector_copy_ok_callback (GtkWidget *widget,
  3327.                    gchar     *copy_name,
  3328.                    gpointer   data)
  3329. {
  3330.   GFlare *gflare;
  3331.   gint    pos;
  3332.  
  3333.   g_assert (copy_name != NULL);
  3334.  
  3335.   if (gflares_list_lookup (copy_name))
  3336.     {
  3337.       g_warning (_("The name `%s' is used already!"), copy_name);
  3338.       return;
  3339.     }
  3340.  
  3341.   gflare = gflare_dup (dlg->gflare, copy_name);
  3342.  
  3343.   pos = gflares_list_insert (gflare);
  3344.   dlg_selector_insert (gflare, pos, 1);
  3345.  
  3346.   dlg->gflare = gflare;
  3347.   gflare_save (dlg->gflare);
  3348.   dlg_preview_update ();
  3349. }
  3350.  
  3351. /*
  3352.  *    "Delete" button in Selector page
  3353.  */
  3354. static void
  3355. dlg_selector_delete_callback (GtkWidget *widget,
  3356.                   gpointer   data)
  3357. {
  3358.   GtkWidget *dialog;
  3359.   gchar        *str;
  3360.  
  3361.   if (num_gflares <= 1)
  3362.     {
  3363.       g_message (_("Cannot delete!! There must be at least one GFlare."));
  3364.       return;
  3365.     }
  3366.  
  3367.   gtk_widget_set_sensitive (dlg->shell, FALSE);
  3368.  
  3369.   str = g_strdup_printf (_("Are you sure you want to delete\n"
  3370.                "\"%s\" from the list and from disk?"),
  3371.              dlg->gflare->name);
  3372.  
  3373.   dialog = gimp_query_boolean_box (_("Delete GFlare"),
  3374.                    gimp_standard_help_func,
  3375.                    "filters/gflare.html",
  3376.                    FALSE,
  3377.                    str,
  3378.                    _("Delete"), _("Cancel"),
  3379.                    NULL, NULL,
  3380.                    dlg_selector_do_delete_callback,
  3381.                    NULL);
  3382.  
  3383.   g_free (str);
  3384.  
  3385.   gtk_widget_show (dialog);
  3386. }
  3387.  
  3388. static void
  3389. dlg_selector_do_delete_callback (GtkWidget *widget,
  3390.                  gboolean   delete,
  3391.                  gpointer   data)
  3392. {
  3393.   GFlare *old_gflare;
  3394.   GList  *tmp;
  3395.   gint    i, new_i;
  3396.  
  3397.   gtk_widget_set_sensitive (dlg->shell, TRUE);
  3398.  
  3399.   if (!delete)
  3400.     return;
  3401.  
  3402.   i = gflares_list_index (dlg->gflare);
  3403.  
  3404.   if (i >= 0)
  3405.     {
  3406.       /* Remove current gflare from gflares_list and free it */
  3407.       old_gflare = dlg->gflare;
  3408.       gflares_list_remove (dlg->gflare);
  3409.       dlg->gflare = NULL;
  3410.  
  3411.       /* Remove from listbox */
  3412.       gtk_list_clear_items (GTK_LIST (dlg->selector_list), i, i + 1);
  3413.  
  3414.       /* Calculate new position of gflare and select it */
  3415.       new_i = (i < num_gflares) ? i : num_gflares - 1;
  3416.       if ((tmp = g_list_nth (gflares_list, new_i)))
  3417.       dlg->gflare = tmp->data;
  3418.       gtk_list_select_item (GTK_LIST (dlg->selector_list), new_i);
  3419.  
  3420.       /* Delete old one from disk and memory */
  3421.       if (old_gflare->filename)
  3422.     unlink (old_gflare->filename);
  3423.       gflare_free (old_gflare);
  3424.  
  3425.       /* Update */
  3426.       dlg_preview_update ();
  3427.     }
  3428.   else
  3429.     {
  3430.       g_warning (_("not found %s in gflares_list"), dlg->gflare->name);
  3431.     }
  3432. }
  3433.  
  3434. /*************************************************************************/
  3435. /**                                    **/
  3436. /**            GFlare Editor                    **/
  3437. /**            +++ ed                        **/
  3438. /**                                    **/
  3439. /*************************************************************************/
  3440.  
  3441. /*
  3442.     This is gflare editor dilaog, one which opens by clicking
  3443.     "Edit" button on the selector page in the main dialog.
  3444.  */
  3445.  
  3446. static void
  3447. ed_run (GFlare             *target_gflare,
  3448.     GFlareEditorCallback  callback,
  3449.     gpointer          calldata)
  3450. {
  3451.   GtkWidget *shell;
  3452.   GtkWidget *hbox;
  3453.   GtkWidget *frame; 
  3454.   GtkWidget *abox;
  3455.   GtkWidget *notebook;
  3456.  
  3457.   if (!ed)
  3458.     ed = g_new0 (GFlareEditor, 1);
  3459.   ed->init          = TRUE;
  3460.   ed->run           = FALSE;
  3461.   ed->target_gflare = target_gflare;
  3462.   ed->gflare        = gflare_dup (target_gflare, target_gflare->name);
  3463.   ed->callback      = callback;
  3464.   ed->calldata      = calldata;
  3465.  
  3466.   /*
  3467.    *    Dialog Shell
  3468.    */
  3469.   shell = ed->shell =
  3470.     gimp_dialog_new (_("GFlare Editor"), "gflare",
  3471.              gimp_standard_help_func, "filters/gflare.html",
  3472.              GTK_WIN_POS_MOUSE,
  3473.              FALSE, TRUE, FALSE,
  3474.  
  3475.              _("Rescan Gradients"), ed_rescan_callback,
  3476.              NULL, NULL, NULL, FALSE, FALSE,
  3477.              _("OK"), ed_ok_callback,
  3478.              NULL, NULL, NULL, TRUE, FALSE,
  3479.              _("Cancel"), gtk_widget_destroy,
  3480.              NULL, 1, NULL, FALSE, TRUE,
  3481.  
  3482.              NULL);
  3483.  
  3484.   gtk_signal_connect (GTK_OBJECT (shell), "destroy",
  3485.               GTK_SIGNAL_FUNC (ed_close_callback),
  3486.               NULL);
  3487.  
  3488.   /*
  3489.    *    main hbox
  3490.    */
  3491.  
  3492.   hbox = gtk_hbox_new (FALSE, 6);
  3493.   gtk_container_set_border_width (GTK_CONTAINER (hbox), 6); 
  3494.   gtk_box_pack_start (GTK_BOX (GTK_DIALOG (shell)->vbox), hbox,
  3495.               FALSE, FALSE, 0);
  3496.   gtk_widget_show (hbox);
  3497.  
  3498.   /*
  3499.    *    Preview
  3500.    */
  3501.  
  3502.   frame = gtk_frame_new (_("Preview")); 
  3503.   gtk_box_pack_start (GTK_BOX (hbox), frame, FALSE, FALSE, 0);
  3504.   gtk_widget_show (frame); 
  3505.  
  3506.   abox = gtk_alignment_new (0.5, 0.5, 0.0, 0.0);
  3507.   gtk_container_set_border_width (GTK_CONTAINER (abox), 4);
  3508.   gtk_container_add (GTK_CONTAINER (frame), abox);
  3509.   gtk_widget_show (abox);
  3510.  
  3511.   frame = gtk_frame_new (NULL); 
  3512.   gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN); 
  3513.   gtk_container_add (GTK_CONTAINER (abox), frame);
  3514.   gtk_widget_show (frame);
  3515.  
  3516.   ed->preview = preview_new (ED_PREVIEW_WIDTH, ED_PREVIEW_HEIGHT,
  3517.                  ed_preview_init_func, NULL,
  3518.                  ed_preview_render_func, NULL,
  3519.                  ed_preview_deinit_func, NULL);
  3520.   gtk_widget_set_events (GTK_WIDGET (ed->preview->widget), DLG_PREVIEW_MASK);
  3521.   gtk_container_add (GTK_CONTAINER (frame), ed->preview->widget); 
  3522.   gtk_signal_connect (GTK_OBJECT(ed->preview->widget), "event",
  3523.               GTK_SIGNAL_FUNC (ed_preview_handle_event),
  3524.               NULL);
  3525.   ed_preview_calc_window ();
  3526.  
  3527.   /*
  3528.    *    Notebook
  3529.    */
  3530.   notebook = ed->notebook = gtk_notebook_new ();
  3531.   gtk_notebook_set_tab_pos (GTK_NOTEBOOK (notebook), GTK_POS_TOP);
  3532.   gtk_box_pack_start (GTK_BOX (hbox), notebook, TRUE, TRUE, 0);
  3533.   gtk_widget_show (notebook);
  3534.  
  3535.   ed_make_page_general (ed, notebook);
  3536.   ed_make_page_glow (ed, notebook);
  3537.   ed_make_page_rays (ed, notebook);
  3538.   ed_make_page_sflare (ed, notebook);
  3539.  
  3540.   gtk_widget_show (shell);
  3541.  
  3542.   ed->init = FALSE;
  3543.   ed_preview_update ();
  3544. }
  3545.  
  3546. static void
  3547. ed_close_callback (GtkWidget *widget,
  3548.            gpointer   data)
  3549. {
  3550.   preview_free (ed->preview);
  3551.   gflare_free (ed->gflare);
  3552.   if (ed->callback)
  3553.     (*ed->callback) (ed->run, ed->calldata);
  3554. }
  3555.  
  3556. static void
  3557. ed_ok_callback (GtkWidget *widget,
  3558.         gpointer   data)
  3559. {
  3560.   ed->run = TRUE;
  3561.   gflare_copy (ed->target_gflare, ed->gflare);
  3562.   gtk_widget_destroy (ed->shell);
  3563. }
  3564.  
  3565. static void
  3566. ed_rescan_callback (GtkWidget *widget,
  3567.             gpointer   data)
  3568. {
  3569.   ed->init = TRUE;
  3570.   gradient_menu_rescan ();
  3571.   ed->init = FALSE;
  3572.   ed_preview_update ();
  3573.   gtk_widget_draw (ed->notebook, NULL);
  3574. }
  3575.  
  3576. static void
  3577. ed_make_page_general (GFlareEditor *ed,
  3578.               GtkWidget    *notebook)
  3579. {
  3580.   GFlare    *gflare = ed->gflare;
  3581.   GtkWidget *vbox;
  3582.   GtkWidget *frame;
  3583.   GtkWidget *table;
  3584.   GtkWidget *option_menu;
  3585.   GtkObject *adj;
  3586.  
  3587.   vbox = gtk_vbox_new (FALSE, 4);
  3588.   gtk_container_set_border_width (GTK_CONTAINER (vbox), 6);
  3589.  
  3590.   /*  Glow  */
  3591.  
  3592.   frame = gtk_frame_new (_("Glow Paint Options"));
  3593.   gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
  3594.   gtk_widget_show (frame);
  3595.  
  3596.   table = gtk_table_new (2, 3, FALSE);
  3597.   gtk_container_set_border_width (GTK_CONTAINER (table), 4);
  3598.   gtk_table_set_row_spacings (GTK_TABLE (table), 2);
  3599.   gtk_table_set_col_spacings (GTK_TABLE (table), 4);
  3600.   gtk_container_add (GTK_CONTAINER (frame), table);
  3601.   gtk_widget_show (table);
  3602.  
  3603.   adj = gimp_scale_entry_new (GTK_TABLE (table), 0, 0,
  3604.                   _("Opacity:"), SCALE_WIDTH, 0,
  3605.                   gflare->glow_opacity, 0.0, 100.0, 1.0, 10.0, 1,
  3606.                   TRUE, 0, 0,
  3607.                   NULL, NULL);
  3608.   gtk_signal_connect (GTK_OBJECT (adj), "value_changed",
  3609.               GTK_SIGNAL_FUNC (gimp_double_adjustment_update),
  3610.               &gflare->glow_opacity);
  3611.   gtk_signal_connect (GTK_OBJECT (adj), "value_changed",
  3612.               GTK_SIGNAL_FUNC (ed_preview_update),
  3613.               NULL);
  3614.  
  3615.   option_menu = ed_mode_menu_new (&gflare->glow_mode);
  3616.   gimp_table_attach_aligned (GTK_TABLE (table), 0, 1,
  3617.                  _("Paint Mode:"), 1.0, 0.5,
  3618.                  option_menu, 1, TRUE);
  3619.  
  3620.   /*  Rays  */
  3621.  
  3622.   frame = gtk_frame_new (_("Rays Paint Options"));
  3623.   gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
  3624.   gtk_widget_show (frame);
  3625.  
  3626.   table = gtk_table_new (2, 3, FALSE);
  3627.   gtk_container_set_border_width (GTK_CONTAINER (table), 4);
  3628.   gtk_table_set_row_spacings (GTK_TABLE (table), 2);
  3629.   gtk_table_set_col_spacings (GTK_TABLE (table), 4);
  3630.   gtk_container_add (GTK_CONTAINER (frame), table);
  3631.   gtk_widget_show (table);
  3632.  
  3633.   adj = gimp_scale_entry_new (GTK_TABLE (table), 0, 0,
  3634.                   _("Opacity:"), SCALE_WIDTH, 0,
  3635.                   gflare->rays_opacity, 0.0, 100.0, 1.0, 10.0, 1,
  3636.                   TRUE, 0, 0,
  3637.                   NULL, NULL);
  3638.   gtk_signal_connect (GTK_OBJECT (adj), "value_changed",
  3639.               GTK_SIGNAL_FUNC (gimp_double_adjustment_update),
  3640.               &gflare->rays_opacity);
  3641.   gtk_signal_connect (GTK_OBJECT (adj), "value_changed",
  3642.               GTK_SIGNAL_FUNC (ed_preview_update),
  3643.               NULL);
  3644.  
  3645.   option_menu = ed_mode_menu_new (&gflare->rays_mode);
  3646.   gimp_table_attach_aligned (GTK_TABLE (table), 0, 1,
  3647.                  _("Paint Mode:"), 1.0, 0.5,
  3648.                  option_menu, 1, TRUE);
  3649.  
  3650.   /*  Rays  */
  3651.  
  3652.   frame = gtk_frame_new (_("Second Flares Paint Options"));
  3653.   gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
  3654.   gtk_widget_show (frame);
  3655.  
  3656.   table = gtk_table_new (2, 3, FALSE);
  3657.   gtk_container_set_border_width (GTK_CONTAINER (table), 4);
  3658.   gtk_table_set_row_spacings (GTK_TABLE (table), 2);
  3659.   gtk_table_set_col_spacings (GTK_TABLE (table), 4);
  3660.   gtk_container_add (GTK_CONTAINER (frame), table);
  3661.   gtk_widget_show (table);
  3662.  
  3663.   adj = gimp_scale_entry_new (GTK_TABLE (table), 0, 0,
  3664.                   _("Opacity:"), SCALE_WIDTH, 0,
  3665.                   gflare->sflare_opacity, 0.0, 100.0, 1.0, 10.0, 1,
  3666.                   TRUE, 0, 0,
  3667.                   NULL, NULL);
  3668.   gtk_signal_connect (GTK_OBJECT (adj), "value_changed",
  3669.               GTK_SIGNAL_FUNC (gimp_double_adjustment_update),
  3670.               &gflare->sflare_opacity);
  3671.   gtk_signal_connect (GTK_OBJECT (adj), "value_changed",
  3672.               GTK_SIGNAL_FUNC (ed_preview_update),
  3673.               NULL);
  3674.  
  3675.   option_menu = ed_mode_menu_new (&gflare->sflare_mode);
  3676.   gimp_table_attach_aligned (GTK_TABLE (table), 0, 1,
  3677.                  _("Paint Mode:"), 1.0, 0.5,
  3678.                  option_menu, 1, TRUE);
  3679.  
  3680.   /*
  3681.    *    Create Page
  3682.    */
  3683.   gtk_notebook_append_page (GTK_NOTEBOOK (notebook), vbox,
  3684.                 gtk_label_new (_("General")));
  3685.   gtk_signal_connect (GTK_OBJECT (vbox), "map",
  3686.               GTK_SIGNAL_FUNC (ed_page_map_callback),
  3687.               (gpointer) PAGE_GENERAL);
  3688.   gtk_widget_show (vbox);
  3689. }
  3690.  
  3691. static void
  3692. ed_make_page_glow (GFlareEditor *ed,
  3693.            GtkWidget    *notebook)
  3694. {
  3695.   GFlare       *gflare = ed->gflare;
  3696.   GradientMenu *gm;
  3697.   GtkWidget    *vbox;
  3698.   GtkWidget    *frame;
  3699.   GtkWidget    *table;
  3700.   GtkObject    *adj;
  3701.   gint          row;
  3702.   
  3703.   vbox = gtk_vbox_new (FALSE, 4);
  3704.   gtk_container_set_border_width (GTK_CONTAINER (vbox), 6);
  3705.  
  3706.   /*
  3707.    *  Gradient Menus
  3708.    */
  3709.  
  3710.   frame = gtk_frame_new (_("Gradients"));
  3711.   gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
  3712.   gtk_widget_show (frame);
  3713.  
  3714.   table = gtk_table_new (3, 3, FALSE);
  3715.   gtk_table_set_row_spacings (GTK_TABLE (table), 2);
  3716.   gtk_table_set_col_spacings (GTK_TABLE (table), 4);
  3717.   gtk_container_set_border_width (GTK_CONTAINER (table), 4);
  3718.   gtk_container_add (GTK_CONTAINER (frame), table);
  3719.  
  3720.   gm = gradient_menu_new ((GradientMenuCallback) &ed_gradient_menu_callback,
  3721.               gflare->glow_radial, gflare->glow_radial);
  3722.   ed_put_gradient_menu (table, 0, 0, _("Radial Gradient:"), gm);
  3723.  
  3724.   gm = gradient_menu_new ((GradientMenuCallback) &ed_gradient_menu_callback,
  3725.               gflare->glow_angular, gflare->glow_angular);
  3726.   ed_put_gradient_menu (table, 0, 1, _("Angular Gradient:"), gm);
  3727.  
  3728.   gm = gradient_menu_new ((GradientMenuCallback) &ed_gradient_menu_callback,
  3729.               gflare->glow_angular_size, gflare->glow_angular_size);
  3730.   ed_put_gradient_menu (table, 0, 2, _("Angular Size Gradient:"), gm);
  3731.  
  3732.   gtk_widget_show (table);
  3733.  
  3734.   /*
  3735.    *  Scales
  3736.    */
  3737.  
  3738.   frame = gtk_frame_new (_("Parameters"));
  3739.   gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
  3740.   gtk_widget_show (frame);
  3741.  
  3742.   table = gtk_table_new (3, 3, FALSE);
  3743.   gtk_table_set_row_spacings (GTK_TABLE (table), 2);
  3744.   gtk_table_set_col_spacings (GTK_TABLE (table), 4);
  3745.   gtk_container_set_border_width (GTK_CONTAINER (table), 4);
  3746.   gtk_container_add (GTK_CONTAINER (frame), table);
  3747.  
  3748.   row = 0;
  3749.  
  3750.   adj = gimp_scale_entry_new (GTK_TABLE (table), 0, row++,
  3751.                   _("Size (%):"), SCALE_WIDTH, 0,
  3752.                   gflare->glow_size, 0.0, 200.0, 1.0, 10.0, 1,
  3753.                   FALSE, 0, G_MAXINT,
  3754.                   NULL, NULL);
  3755.   gtk_signal_connect (GTK_OBJECT (adj), "value_changed",
  3756.               GTK_SIGNAL_FUNC (gimp_double_adjustment_update),
  3757.               &gflare->glow_size);
  3758.   gtk_signal_connect (GTK_OBJECT (adj), "value_changed",
  3759.               GTK_SIGNAL_FUNC (ed_preview_update),
  3760.               NULL);
  3761.  
  3762.   adj = gimp_scale_entry_new (GTK_TABLE (table), 0, row++,
  3763.                   _("Rotation:"), SCALE_WIDTH, 0,
  3764.                   gflare->glow_rotation, -180.0, 180.0, 1.0, 15.0, 1,
  3765.                   TRUE, 0, 0,
  3766.                   NULL, NULL);
  3767.   gtk_signal_connect (GTK_OBJECT (adj), "value_changed",
  3768.               GTK_SIGNAL_FUNC (gimp_double_adjustment_update),
  3769.               &gflare->glow_rotation);
  3770.   gtk_signal_connect (GTK_OBJECT (adj), "value_changed",
  3771.               GTK_SIGNAL_FUNC (ed_preview_update),
  3772.               NULL);
  3773.  
  3774.   adj = gimp_scale_entry_new (GTK_TABLE (table), 0, row++,
  3775.                   _("Hue Rotation:"), SCALE_WIDTH, 0,
  3776.                   gflare->glow_hue, -180.0, 180.0, 1.0, 15.0, 1,
  3777.                   TRUE, 0, 0,
  3778.                   NULL, NULL);
  3779.   gtk_signal_connect (GTK_OBJECT (adj), "value_changed",
  3780.               GTK_SIGNAL_FUNC (gimp_double_adjustment_update),
  3781.               &gflare->glow_hue);
  3782.   gtk_signal_connect (GTK_OBJECT (adj), "value_changed",
  3783.               GTK_SIGNAL_FUNC (ed_preview_update),
  3784.               NULL);
  3785.  
  3786.   gtk_widget_show (table);
  3787.  
  3788.   /*
  3789.    *  Create Page
  3790.    */
  3791.   gtk_notebook_append_page (GTK_NOTEBOOK (notebook), vbox,
  3792.                 gtk_label_new (_("Glow")));
  3793.   gtk_signal_connect (GTK_OBJECT (vbox), "map",
  3794.               GTK_SIGNAL_FUNC (ed_page_map_callback),
  3795.               (gpointer) PAGE_GLOW);
  3796.   gtk_widget_show (vbox);
  3797. }
  3798.  
  3799. static void
  3800. ed_make_page_rays (GFlareEditor *ed,
  3801.            GtkWidget    *notebook)
  3802. {
  3803.   GFlare       *gflare = ed->gflare;
  3804.   GradientMenu *gm;
  3805.   GtkWidget    *vbox;
  3806.   GtkWidget    *frame;
  3807.   GtkWidget    *table;
  3808.   GtkObject    *adj;
  3809.   gint          row;
  3810.  
  3811.   vbox = gtk_vbox_new (FALSE, 4);
  3812.   gtk_container_set_border_width (GTK_CONTAINER (vbox), 6);
  3813.  
  3814.   /*
  3815.    *  Gradient Menus
  3816.    */
  3817.  
  3818.   frame = gtk_frame_new (_("Gradients"));
  3819.   gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
  3820.   gtk_widget_show (frame);
  3821.  
  3822.   table = gtk_table_new (3, 3, FALSE);
  3823.   gtk_table_set_row_spacings (GTK_TABLE (table), 2);
  3824.   gtk_table_set_col_spacings (GTK_TABLE (table), 4);
  3825.   gtk_container_set_border_width (GTK_CONTAINER (table), 4);
  3826.   gtk_container_add (GTK_CONTAINER (frame), table);
  3827.  
  3828.   row = 0;
  3829.  
  3830.   gm = gradient_menu_new ((GradientMenuCallback) &ed_gradient_menu_callback,
  3831.               gflare->rays_radial, gflare->rays_radial);
  3832.   ed_put_gradient_menu (table, 0, row++, _("Radial Gradient:"), gm);
  3833.  
  3834.   gm = gradient_menu_new ((GradientMenuCallback) &ed_gradient_menu_callback,
  3835.               gflare->rays_angular, gflare->rays_angular);
  3836.   ed_put_gradient_menu (table, 0, row++, _("Angular Gradient:"), gm);
  3837.  
  3838.   gm = gradient_menu_new ((GradientMenuCallback) &ed_gradient_menu_callback,
  3839.               gflare->rays_angular_size, gflare->rays_angular_size);
  3840.   ed_put_gradient_menu (table, 0, row++, _("Angular Size Gradient:"), gm);
  3841.  
  3842.   gtk_widget_show (table);
  3843.  
  3844.   /*
  3845.    *    Scales
  3846.    */
  3847.  
  3848.   frame = gtk_frame_new (_("Parameters"));
  3849.   gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
  3850.   gtk_widget_show (frame);
  3851.  
  3852.   table = gtk_table_new (5, 3, FALSE);
  3853.   gtk_container_set_border_width (GTK_CONTAINER (table), 4);
  3854.   gtk_table_set_row_spacings (GTK_TABLE (table), 2);
  3855.   gtk_table_set_col_spacings (GTK_TABLE (table), 4);
  3856.   gtk_container_add (GTK_CONTAINER (frame), table);
  3857.  
  3858.   row = 0;
  3859.  
  3860.   adj = gimp_scale_entry_new (GTK_TABLE (table), 0, row++,
  3861.                   _("Size (%):"), SCALE_WIDTH, 0,
  3862.                   gflare->rays_size, 0.0, 200.0, 1.0, 10.0, 1,
  3863.                   FALSE, 0, G_MAXINT,
  3864.                   NULL, NULL);
  3865.   gtk_signal_connect (GTK_OBJECT (adj), "value_changed",
  3866.               GTK_SIGNAL_FUNC (gimp_double_adjustment_update),
  3867.               &gflare->rays_size);
  3868.   gtk_signal_connect (GTK_OBJECT (adj), "value_changed",
  3869.               GTK_SIGNAL_FUNC (ed_preview_update),
  3870.               NULL);
  3871.  
  3872.   adj = gimp_scale_entry_new (GTK_TABLE (table), 0, row++,
  3873.                   _("Rotation:"), SCALE_WIDTH, 0,
  3874.                   gflare->rays_rotation,
  3875.                   -180.0, 180.0, 1.0, 15.0, 1,
  3876.                   TRUE, 0, 0,
  3877.                   NULL, NULL);
  3878.   gtk_signal_connect (GTK_OBJECT (adj), "value_changed",
  3879.               GTK_SIGNAL_FUNC (gimp_double_adjustment_update),
  3880.               &gflare->rays_rotation);
  3881.   gtk_signal_connect (GTK_OBJECT (adj), "value_changed",
  3882.               GTK_SIGNAL_FUNC (ed_preview_update),
  3883.               NULL);
  3884.  
  3885.   adj = gimp_scale_entry_new (GTK_TABLE (table), 0, row++,
  3886.                   _("Hue Rotation:"), SCALE_WIDTH, 0,
  3887.                   gflare->rays_hue, -180.0, 180.0, 1.0, 15.0, 1,
  3888.                   TRUE, 0, 0,
  3889.                   NULL, NULL);
  3890.   gtk_signal_connect (GTK_OBJECT (adj), "value_changed",
  3891.               GTK_SIGNAL_FUNC (gimp_double_adjustment_update),
  3892.               &gflare->rays_hue);
  3893.   gtk_signal_connect (GTK_OBJECT (adj), "value_changed",
  3894.               GTK_SIGNAL_FUNC (ed_preview_update),
  3895.               NULL);
  3896.  
  3897.   adj = gimp_scale_entry_new (GTK_TABLE (table), 0, row++,
  3898.                   _("# of Spikes:"), SCALE_WIDTH, 0,
  3899.                   gflare->rays_nspikes, 1, 300, 1.0, 10.0, 0,
  3900.                   FALSE, 0, G_MAXINT,
  3901.                   NULL, NULL);
  3902.   gtk_signal_connect (GTK_OBJECT (adj), "value_changed",
  3903.               GTK_SIGNAL_FUNC (gimp_int_adjustment_update),
  3904.               &gflare->rays_nspikes);
  3905.   gtk_signal_connect (GTK_OBJECT (adj), "value_changed",
  3906.               GTK_SIGNAL_FUNC (ed_preview_update),
  3907.               NULL);
  3908.  
  3909.   adj = gimp_scale_entry_new (GTK_TABLE (table), 0, row++,
  3910.                   _("Spike Thickness:"), SCALE_WIDTH, 0,
  3911.                   gflare->rays_thickness, 1.0, 100.0, 1.0, 10.0, 1,
  3912.                   FALSE, 0, GIMP_MAX_IMAGE_SIZE,
  3913.                   NULL, NULL);
  3914.   gtk_signal_connect (GTK_OBJECT (adj), "value_changed",
  3915.               GTK_SIGNAL_FUNC (gimp_double_adjustment_update),
  3916.               &gflare->rays_thickness);
  3917.   gtk_signal_connect (GTK_OBJECT (adj), "value_changed",
  3918.               GTK_SIGNAL_FUNC (ed_preview_update),
  3919.               NULL);
  3920.  
  3921.   gtk_widget_show (table);
  3922.  
  3923.   /*
  3924.    *    Create Pages
  3925.    */
  3926.   gtk_notebook_append_page (GTK_NOTEBOOK (notebook), vbox,
  3927.                 gtk_label_new (_("Rays")));
  3928.   gtk_signal_connect (GTK_OBJECT (vbox), "map",
  3929.               GTK_SIGNAL_FUNC (ed_page_map_callback),
  3930.               (gpointer) PAGE_RAYS);
  3931.   gtk_widget_show (vbox);
  3932. }
  3933.  
  3934. static void
  3935. ed_make_page_sflare (GFlareEditor *ed,
  3936.              GtkWidget    *notebook)
  3937. {
  3938.   GFlare       *gflare = ed->gflare;
  3939.   GradientMenu *gm;
  3940.   GtkWidget    *vbox;
  3941.   GtkWidget    *table;
  3942.   GtkWidget    *frame;
  3943.   GtkWidget    *shape_vbox;
  3944.   GSList       *shape_group = NULL;
  3945.   GtkWidget    *polygon_hbox;
  3946.   GtkWidget    *seed_hbox;
  3947.   GtkWidget    *toggle;
  3948.   GtkWidget    *label;
  3949.   GtkWidget    *seed;
  3950.   GtkWidget    *entry;
  3951.   GtkObject    *adj;
  3952.   gchar         buf[256];
  3953.   gint          row;
  3954.  
  3955.   vbox = gtk_vbox_new (FALSE, 4);
  3956.   gtk_container_set_border_width (GTK_CONTAINER (vbox), 6);
  3957.  
  3958.   /*
  3959.    *  Gradient Menus
  3960.    */
  3961.  
  3962.   frame = gtk_frame_new (_("Gradients"));
  3963.   gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
  3964.   gtk_widget_show (frame);
  3965.  
  3966.   table = gtk_table_new (3, 3, FALSE);
  3967.   gtk_container_set_border_width (GTK_CONTAINER (table), 4);
  3968.   gtk_table_set_row_spacings (GTK_TABLE (table), 2);
  3969.   gtk_table_set_col_spacings (GTK_TABLE (table), 4);
  3970.   gtk_container_add (GTK_CONTAINER (frame), table);
  3971.  
  3972.   gm = gradient_menu_new ((GradientMenuCallback) &ed_gradient_menu_callback,
  3973.               gflare->sflare_radial, gflare->sflare_radial);
  3974.   ed_put_gradient_menu (table, 0, 0, _("Radial Gradient:"), gm);
  3975.  
  3976.   gm = gradient_menu_new ((GradientMenuCallback) &ed_gradient_menu_callback,
  3977.               gflare->sflare_sizefac, gflare->sflare_sizefac);
  3978.   ed_put_gradient_menu (table, 0, 1, _("Size Factor Gradient:"), gm);
  3979.  
  3980.   gm = gradient_menu_new ((GradientMenuCallback) &ed_gradient_menu_callback,
  3981.               gflare->sflare_probability, gflare->sflare_probability);
  3982.   ed_put_gradient_menu (table, 0, 2, _("Probability Gradient:"), gm);
  3983.  
  3984.   gtk_widget_show (table);
  3985.  
  3986.   /*
  3987.    *    Scales
  3988.    */
  3989.  
  3990.   frame = gtk_frame_new (_("Parameters"));
  3991.   gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
  3992.   gtk_widget_show (frame);
  3993.  
  3994.   table = gtk_table_new (3, 3, FALSE);
  3995.   gtk_container_set_border_width (GTK_CONTAINER (table), 4);
  3996.   gtk_table_set_row_spacings (GTK_TABLE (table), 2);
  3997.   gtk_table_set_col_spacings (GTK_TABLE (table), 4);
  3998.   gtk_container_add (GTK_CONTAINER (frame), table);
  3999.  
  4000.   row = 0;
  4001.  
  4002.   adj = gimp_scale_entry_new (GTK_TABLE (table), 0, row++,
  4003.                   _("Size (%):"), SCALE_WIDTH, 0,
  4004.                   gflare->sflare_size, 0.0, 200.0, 1.0, 10.0, 1,
  4005.                   FALSE, 0, G_MAXINT,
  4006.                   NULL, NULL);
  4007.   gtk_signal_connect (GTK_OBJECT (adj), "value_changed",
  4008.               GTK_SIGNAL_FUNC (gimp_double_adjustment_update),
  4009.               &gflare->sflare_size);
  4010.   gtk_signal_connect (GTK_OBJECT (adj), "value_changed",
  4011.               GTK_SIGNAL_FUNC (ed_preview_update),
  4012.               NULL);
  4013.  
  4014.   adj = gimp_scale_entry_new (GTK_TABLE (table), 0, row++,
  4015.                   _("Rotation:"), SCALE_WIDTH, 0,
  4016.                   gflare->sflare_rotation,
  4017.                   -180.0, 180.0, 1.0, 15.0, 1,
  4018.                   TRUE, 0, 0,
  4019.                   NULL, NULL);
  4020.   gtk_signal_connect (GTK_OBJECT (adj), "value_changed",
  4021.               GTK_SIGNAL_FUNC (gimp_double_adjustment_update),
  4022.               &gflare->sflare_rotation);
  4023.   gtk_signal_connect (GTK_OBJECT (adj), "value_changed",
  4024.               GTK_SIGNAL_FUNC (ed_preview_update),
  4025.               NULL);
  4026.  
  4027.   adj = gimp_scale_entry_new (GTK_TABLE (table), 0, row++,
  4028.                   _("Hue Rotation:"), SCALE_WIDTH, 0,
  4029.                   gflare->sflare_hue, -180.0, 180.0, 1.0, 15.0, 1,
  4030.                   TRUE, 0, 0,
  4031.                   NULL, NULL);
  4032.   gtk_signal_connect (GTK_OBJECT (adj), "value_changed",
  4033.               GTK_SIGNAL_FUNC (gimp_double_adjustment_update),
  4034.               &gflare->sflare_hue);
  4035.   gtk_signal_connect (GTK_OBJECT (adj), "value_changed",
  4036.               GTK_SIGNAL_FUNC (ed_preview_update),
  4037.               NULL);
  4038.  
  4039.   gtk_widget_show (table);
  4040.  
  4041.   /*
  4042.    *    Shape Radio Button Frame
  4043.    */
  4044.  
  4045.   frame = gtk_frame_new (_("Shape of Second Flares"));
  4046.   gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
  4047.   gtk_widget_show (frame);
  4048.  
  4049.   shape_vbox = gtk_vbox_new (FALSE, 1);
  4050.   gtk_container_set_border_width (GTK_CONTAINER (shape_vbox), 4);
  4051.   gtk_container_add (GTK_CONTAINER (frame), shape_vbox);
  4052.   gtk_widget_show (shape_vbox);
  4053.  
  4054.   toggle = gtk_radio_button_new_with_label (shape_group, _("Circle"));
  4055.   shape_group = gtk_radio_button_group (GTK_RADIO_BUTTON (toggle));
  4056.   gtk_object_set_user_data (GTK_OBJECT (toggle), (gpointer) GF_CIRCLE);
  4057.   gtk_signal_connect (GTK_OBJECT (toggle), "toggled",
  4058.               GTK_SIGNAL_FUNC (ed_shape_radio_callback),
  4059.               &gflare->sflare_shape);
  4060.   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle),
  4061.                 gflare->sflare_shape == GF_CIRCLE);
  4062.   gtk_box_pack_start (GTK_BOX (shape_vbox), toggle, FALSE, FALSE, 0);
  4063.   gtk_widget_show (toggle);
  4064.  
  4065.   polygon_hbox = gtk_hbox_new (FALSE, 4);
  4066.   gtk_box_pack_start (GTK_BOX (shape_vbox), polygon_hbox, FALSE, FALSE, 0);
  4067.   gtk_widget_show (polygon_hbox);
  4068.  
  4069.   toggle = ed->polygon_toggle =
  4070.     gtk_radio_button_new_with_label (shape_group, _("Polygon"));
  4071.   shape_group = gtk_radio_button_group (GTK_RADIO_BUTTON (toggle));
  4072.   gtk_object_set_user_data (GTK_OBJECT (toggle), (gpointer) GF_POLYGON);
  4073.   gtk_signal_connect (GTK_OBJECT (toggle), "toggled",
  4074.               GTK_SIGNAL_FUNC (ed_shape_radio_callback),
  4075.               &gflare->sflare_shape);
  4076.   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle),
  4077.                 gflare->sflare_shape == GF_POLYGON);
  4078.   gtk_box_pack_start (GTK_BOX (polygon_hbox), toggle, FALSE, FALSE, 0);
  4079.   gtk_widget_show (toggle);
  4080.  
  4081.   entry = ed->polygon_entry = gtk_entry_new ();
  4082.   gtk_widget_set_usize (entry, ENTRY_WIDTH, 0);
  4083.   sprintf (buf, "%d", gflare->sflare_nverts);
  4084.   gtk_entry_set_text (GTK_ENTRY (entry), buf);
  4085.   gtk_signal_connect (GTK_OBJECT (entry), "changed",
  4086.               (GtkSignalFunc) ed_ientry_callback,
  4087.               &gflare->sflare_nverts);
  4088.   gtk_box_pack_start (GTK_BOX (polygon_hbox), entry, FALSE, FALSE, 0);
  4089.   gtk_widget_show (entry);
  4090.  
  4091.   gtk_widget_set_sensitive (entry, gflare->sflare_shape == GF_POLYGON);
  4092.   gtk_object_set_data (GTK_OBJECT (toggle), "set_sensitive", entry);
  4093.  
  4094.   /*
  4095.    *    Random Seed Entry
  4096.    */
  4097.  
  4098.   seed_hbox = gtk_hbox_new (FALSE, 4);
  4099.   gtk_box_pack_start (GTK_BOX (vbox), seed_hbox, FALSE, FALSE, 0);
  4100.   gtk_widget_show (seed_hbox);
  4101.  
  4102.   label = gtk_label_new (_("Random Seed:"));
  4103.   gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
  4104.   gtk_box_pack_start (GTK_BOX (seed_hbox), label, FALSE, FALSE, 0);
  4105.   gtk_widget_show (label);
  4106.  
  4107.   seed = gimp_random_seed_new (&gflare->sflare_seed,
  4108.                    &gflare->sflare_seed_time,
  4109.                    TRUE, FALSE);
  4110.  
  4111.   entry = GTK_WIDGET (GIMP_RANDOM_SEED_SPINBUTTON (seed));
  4112.   toggle = GTK_WIDGET (GIMP_RANDOM_SEED_TOGGLEBUTTON (seed));
  4113.  
  4114.   gtk_box_pack_start (GTK_BOX (seed_hbox), seed, FALSE, TRUE, 0);
  4115.   gtk_widget_show (seed);
  4116.  
  4117.   gtk_signal_connect (GTK_OBJECT (GTK_SPIN_BUTTON (entry)->adjustment),
  4118.               "value_changed",
  4119.               GTK_SIGNAL_FUNC (ed_preview_update),
  4120.               NULL);
  4121.   gtk_signal_connect (GTK_OBJECT (toggle), "toggled",
  4122.               GTK_SIGNAL_FUNC (ed_preview_update),
  4123.               NULL);
  4124.  
  4125.   /*
  4126.    *    Create Pages
  4127.    */
  4128.   gtk_notebook_append_page (GTK_NOTEBOOK (notebook), vbox,
  4129.                 gtk_label_new (_("Second Flares")));
  4130.   gtk_signal_connect (GTK_OBJECT (vbox), "map",
  4131.               GTK_SIGNAL_FUNC (ed_page_map_callback),
  4132.               (gpointer) PAGE_SFLARE);
  4133.   gtk_widget_show (vbox);
  4134. }
  4135.  
  4136. GtkWidget *
  4137. ed_mode_menu_new (GFlareMode *mode_var)
  4138. {
  4139.   GtkWidget    *option_menu;
  4140.   GtkWidget    *menu;
  4141.   GtkWidget    *menuitem;
  4142.   gint        i;
  4143.   GFlareMode    mode;
  4144.  
  4145.   option_menu = gtk_option_menu_new ();
  4146.   menu = gtk_menu_new ();
  4147.  
  4148.   for (i = 0; i < GF_NUM_MODES; i++)
  4149.     {
  4150.       menuitem = gtk_menu_item_new_with_label (gettext (gflare_menu_modes[i]));
  4151.  
  4152.       gtk_object_set_user_data (GTK_OBJECT (menuitem),
  4153.                 GINT_TO_POINTER (i));
  4154.       gtk_signal_connect (GTK_OBJECT (menuitem), "activate",
  4155.               GTK_SIGNAL_FUNC (ed_mode_menu_callback),
  4156.               mode_var);
  4157.       gtk_widget_show (menuitem);
  4158.       gtk_menu_append (GTK_MENU (menu), menuitem);
  4159.     }
  4160.   mode = *mode_var;
  4161.   if (mode < 0 || mode >= GF_NUM_MODES)
  4162.     mode = GF_NORMAL;
  4163.   gtk_menu_set_active (GTK_MENU (menu), mode);
  4164.   gtk_option_menu_set_menu (GTK_OPTION_MENU (option_menu), menu);
  4165.   gtk_widget_show (option_menu);
  4166.  
  4167.   return option_menu;
  4168. }
  4169.  
  4170. /*
  4171.   puts gradient menu with caption into table
  4172.   occupies 1 row and 3 cols in table
  4173.  */
  4174. static void
  4175. ed_put_gradient_menu (GtkWidget    *table,
  4176.               gint          x,
  4177.               gint          y,
  4178.               gchar        *caption,
  4179.               GradientMenu *gm)
  4180. {
  4181.   GtkWidget    *label;
  4182.  
  4183.   label = gtk_label_new (caption);
  4184.   gtk_misc_set_alignment (GTK_MISC (label), 1.0, 0.5);
  4185.   gtk_widget_show (label);
  4186.  
  4187.   gtk_object_set_user_data (GTK_OBJECT (gm->option_menu), ed);
  4188.   gtk_table_attach (GTK_TABLE (table), label,
  4189.             x     , x + 1, y, y + 1,
  4190.             GTK_FILL, 0, 0, 0);
  4191.   gtk_table_attach (GTK_TABLE (table), gm->preview,
  4192.             x + 1, x + 2, y, y + 1,
  4193.             0, 0, 0, 0);
  4194.   gtk_table_attach (GTK_TABLE (table), gm->option_menu,
  4195.             x + 2, x + 3, y, y + 1,
  4196.             0, 0, 0, 0);
  4197. }
  4198.  
  4199. static void
  4200. ed_mode_menu_callback (GtkWidget *widget, gpointer data)
  4201. {
  4202.   GFlareMode    *mode_var;
  4203.  
  4204.   mode_var = data;
  4205.   *mode_var = (GFlareMode) gtk_object_get_user_data (GTK_OBJECT (widget));
  4206.  
  4207.   ed_preview_update ();
  4208. }
  4209.  
  4210. static void
  4211. ed_gradient_menu_callback (gchar *gradient_name, gpointer data)
  4212. {
  4213.   gchar            *dest_string = data;
  4214.  
  4215.   /* @GRADIENT_NAME */
  4216.   gradient_name_copy (dest_string, gradient_name);
  4217.   ed_preview_update ();
  4218. }
  4219.  
  4220. static void
  4221. ed_shape_radio_callback (GtkWidget *widget,
  4222.              gpointer   data)
  4223. {
  4224.   gimp_radio_button_update (widget, data);
  4225.  
  4226.   ed_preview_update ();
  4227. }
  4228.  
  4229. static void
  4230. ed_ientry_callback (GtkWidget *widget, gpointer data)
  4231. {
  4232.   gint        new_val;
  4233.  
  4234.   new_val = atoi (gtk_entry_get_text (GTK_ENTRY (widget)));
  4235.   *(gint *)data = new_val;
  4236.  
  4237.   ed_preview_update ();
  4238. }
  4239.  
  4240. /*
  4241.   NOTE: This is hack, because this code depends on internal "map"
  4242.   signal of changing pages of gtknotebook.
  4243.  */
  4244. static void
  4245. ed_page_map_callback (GtkWidget *widget, gpointer data)
  4246. {
  4247.   gint        page_num = (gint) data;
  4248.  
  4249.   DEBUG_PRINT(("ed_page_map_callback\n"));
  4250.  
  4251.   ed->cur_page = page_num;
  4252.   ed_preview_update ();
  4253. }
  4254.  
  4255.  
  4256. static void
  4257. ed_preview_update ()
  4258. {
  4259.   if (ed->init)
  4260.     return;
  4261.  
  4262.   ed->init_params_done = FALSE;
  4263.   preview_render_start (ed->preview);
  4264. }
  4265.  
  4266. static gint
  4267. ed_preview_init_func (Preview *preview, gpointer data)
  4268. {
  4269.   int    type = 0;
  4270.  
  4271.   if (ed->init_params_done == FALSE)
  4272.     {
  4273.       switch (ed->cur_page)
  4274.     {
  4275.     case PAGE_GENERAL:
  4276.       type = (CALC_GLOW | CALC_RAYS | CALC_SFLARE);
  4277.       break;
  4278.     case PAGE_GLOW:
  4279.       type = CALC_GLOW;
  4280.       break;
  4281.     case PAGE_RAYS:
  4282.       type = CALC_RAYS;
  4283.       break;
  4284.     case PAGE_SFLARE:
  4285.       type = CALC_SFLARE;
  4286.       break;
  4287.     default:
  4288.       g_warning ("ed_preview_edit_func: bad page");
  4289.       break;
  4290.     }
  4291.       calc_init_params (ed->gflare, type,
  4292.             ED_PREVIEW_WIDTH/2, ED_PREVIEW_HEIGHT/2,
  4293.             ED_PREVIEW_WIDTH/2, 0.0, 0.0,
  4294.             pvals.vangle, pvals.vlength);
  4295.  
  4296.       ed->init_params_done = TRUE;
  4297.       return TRUE;
  4298.     }
  4299.   return calc_init_progress ();
  4300. }
  4301.  
  4302. static void
  4303. ed_preview_deinit_func (Preview *preview, gpointer data)
  4304. {
  4305.   if (ed->init_params_done)
  4306.     {
  4307.       calc_deinit ();
  4308.       ed->init_params_done = FALSE;
  4309.     }
  4310. }
  4311.  
  4312. static void
  4313. ed_preview_render_func (Preview *preview, guchar *buffer, gint y, gpointer data)
  4314. {
  4315.   switch (ed->cur_page)
  4316.     {
  4317.     case PAGE_GENERAL:
  4318.       ed_preview_render_general (buffer, y);
  4319.       break;
  4320.     case PAGE_GLOW:
  4321.       ed_preview_render_glow (buffer, y);
  4322.       break;
  4323.     case PAGE_RAYS:
  4324.       ed_preview_render_rays (buffer, y);
  4325.       break;
  4326.     case PAGE_SFLARE:
  4327.       ed_preview_render_sflare (buffer, y);
  4328.       break;
  4329.     default:
  4330.       g_warning ("hmm, bad page in ed_preview_render_func ()");
  4331.       break;
  4332.     }
  4333. }
  4334.  
  4335. static void
  4336. ed_preview_render_general (guchar *buffer, gint y)
  4337. {
  4338.   int        x, i;
  4339.   guchar    gflare_pix[4];
  4340.   static guchar src_pix[4] = {0, 0, 0, OPAQUE};
  4341.   int        gflare_a;
  4342.  
  4343.   for (x = 0; x < ED_PREVIEW_WIDTH; x++)
  4344.     {
  4345.       calc_gflare_pix (gflare_pix, x, y, src_pix);
  4346.       gflare_a = gflare_pix[3];
  4347.  
  4348.       for (i = 0; i < 3; i++)
  4349.     {
  4350.       *buffer++ = gflare_pix[i] * gflare_a / 255;
  4351.     }
  4352.     }
  4353. }
  4354.  
  4355.  
  4356. static void
  4357. ed_preview_render_glow (guchar *buffer, gint y)
  4358. {
  4359.   int        x, i;
  4360.   guchar    pix[4];
  4361.  
  4362.   for (x = 0; x < ED_PREVIEW_WIDTH; x++)
  4363.     {
  4364.       calc_glow_pix (pix, x, y);
  4365.       for (i = 0; i < 3; i++)
  4366.     *buffer++ = pix[i] * pix[3] / 255;
  4367.     }
  4368. }
  4369.  
  4370. static void
  4371. ed_preview_render_rays (guchar *buffer, gint y)
  4372. {
  4373.   int        x, i;
  4374.   guchar    pix[4];
  4375.  
  4376.   for (x = 0; x < ED_PREVIEW_WIDTH; x++)
  4377.     {
  4378.       calc_rays_pix (pix, x, y);
  4379.       for (i = 0; i < 3; i++)
  4380.     *buffer++ = pix[i] * pix[3] / 255;
  4381.     }
  4382. }
  4383.  
  4384. static void
  4385. ed_preview_render_sflare (guchar *buffer, gint y)
  4386. {
  4387.   int        x, i;
  4388.   guchar    pix[4];
  4389.   static guchar src_pix[4] = {0, 0, 0, OPAQUE};
  4390.  
  4391.   for (x = 0; x < ED_PREVIEW_WIDTH; x++)
  4392.     {
  4393.       calc_sflare_pix (pix, x, y, src_pix);
  4394.       for (i = 0; i < 3; i++)
  4395.     *buffer++ = pix[i] * pix[3] / 255;
  4396.     }
  4397. }
  4398.  
  4399. /*************************************************************************/
  4400. /**                                    **/
  4401. /**            +++ Preview                    **/
  4402. /**                                    **/
  4403. /*************************************************************************/
  4404.  
  4405. /*
  4406.     this is generic preview routines.
  4407.  */
  4408.  
  4409.  
  4410. /*
  4411.     Routines to render the preview in background
  4412.  */
  4413. Preview *
  4414. preview_new (gint           width,
  4415.          gint           height,
  4416.          PreviewInitFunc       init_func,
  4417.          gpointer           init_data,
  4418.          PreviewRenderFunc       render_func,
  4419.          gpointer           render_data,
  4420.          PreviewDeinitFunc       deinit_func,
  4421.          gpointer           deinit_data)
  4422. {
  4423.   Preview *preview;
  4424.  
  4425.   preview = g_new0 (Preview, 1);
  4426.  
  4427.   preview->widget = gtk_preview_new (GTK_PREVIEW_COLOR);
  4428.   gtk_object_set_user_data (GTK_OBJECT (preview->widget), preview);
  4429.   gtk_preview_size (GTK_PREVIEW (preview->widget), width, height);
  4430.   gtk_widget_show (preview->widget);
  4431.  
  4432.   preview->width       = width;
  4433.   preview->height       = height;
  4434.   preview->init_func       = init_func;
  4435.   preview->init_data       = init_data;
  4436.   preview->render_func       = render_func;
  4437.   preview->render_data       = render_data;
  4438.   preview->deinit_func       = deinit_func;
  4439.   preview->deinit_data       = deinit_data;
  4440.   preview->idle_tag       = 0;
  4441.   preview->buffer       = g_new (guchar, width * 3);
  4442.  
  4443.   return preview;
  4444. }
  4445.  
  4446. void
  4447. preview_free (Preview *preview)
  4448. {
  4449.   preview_render_end (preview);
  4450.   /* not destroy preview->widget */
  4451.   g_free (preview->buffer);
  4452.   g_free (preview);
  4453. }
  4454.  
  4455.  
  4456. /*
  4457.   Start rendering of the preview in background using an idle event.
  4458.   If already started and not yet finished, stop it first.
  4459.  */
  4460. void
  4461. preview_render_start (Preview *preview)
  4462. {
  4463.   DEBUG_PRINT(("preview_render_start\n"));
  4464.  
  4465.   preview_render_end (preview);
  4466.  
  4467.   preview->init_done = FALSE;
  4468.   preview->current_y = 0;
  4469.   preview->drawn_y = 0;
  4470.   preview->timeout_tag = gtk_timeout_add (100, (GtkFunction) preview_render_start_2, preview);
  4471. }
  4472.  
  4473. static gint
  4474. preview_render_start_2 (Preview *preview)
  4475. {
  4476.   preview->timeout_tag = 0;
  4477.   preview->idle_tag = gtk_idle_add ((GtkFunction) preview_handle_idle, preview);
  4478.   return FALSE;
  4479. }
  4480.  
  4481.  
  4482. void
  4483. preview_render_end (Preview *preview)
  4484. {
  4485.   if (preview->timeout_tag > 0)
  4486.     {
  4487.       gtk_timeout_remove (preview->timeout_tag);
  4488.       preview->timeout_tag = 0;
  4489.     }
  4490.   if (preview->idle_tag > 0)
  4491.     {
  4492.       if (preview->deinit_func)
  4493.     (*preview->deinit_func) (preview, preview->deinit_data);
  4494.  
  4495.       gtk_idle_remove (preview->idle_tag);
  4496.       preview->idle_tag = 0;
  4497.       DEBUG_PRINT(("preview_render_end\n\n"));
  4498.     }
  4499. }
  4500.  
  4501. /*
  4502.   Handle an idle event.
  4503.   Return FALSE if done, TRUE otherwise.
  4504.  */
  4505. static gint
  4506. preview_handle_idle (Preview *preview)
  4507. {
  4508.   gint            done = FALSE;
  4509.   GdkRectangle        draw_rect;
  4510.  
  4511.   if (preview->init_done == FALSE)
  4512.     {
  4513.       if (preview->init_func &&
  4514.       (*preview->init_func) (preview, preview->init_data))
  4515.     return TRUE;
  4516.       preview->init_done = TRUE;
  4517.     }
  4518.  
  4519.   if (preview->render_func)
  4520.     (*preview->render_func) (preview, preview->buffer, preview->current_y,
  4521.                  preview->render_data);
  4522.   else
  4523.     memset (preview->buffer, 0, preview->width * 3);
  4524.  
  4525.   gtk_preview_draw_row (GTK_PREVIEW (preview->widget), preview->buffer,
  4526.             0, preview->current_y, preview->width);
  4527.  
  4528.   if (++preview->current_y >= preview->height)
  4529.     done = TRUE;
  4530.  
  4531.   if (done || preview->current_y % 20 == 0)
  4532.     {
  4533.       draw_rect.x      = 0;
  4534.       draw_rect.y      = preview->drawn_y;
  4535.       draw_rect.width  = preview->width;
  4536.       draw_rect.height = preview->current_y - preview->drawn_y;
  4537.       preview->drawn_y = preview->current_y;
  4538.       gtk_widget_draw (preview->widget, &draw_rect);
  4539.     }
  4540.  
  4541.   if (done)
  4542.     {
  4543.       preview_render_end (preview);
  4544.       return FALSE;
  4545.     }
  4546.  
  4547.   return TRUE;
  4548. }
  4549.  
  4550. /*
  4551.   Convert RGBA to RGB with rendering gray check if needed.
  4552.     (from nova.c)
  4553.   input:  guchar src[4]        RGBA pixel
  4554.   output: guchar dest[3]    RGB pixel
  4555.  */
  4556.  
  4557. void
  4558. preview_rgba_to_rgb (guchar *dest, gint x, gint y, guchar *src)
  4559. {
  4560.   gint src_a;
  4561.   gint check;
  4562.   gint b;
  4563.  
  4564.   src_a = src[3];
  4565.  
  4566.   if (src_a == OPAQUE)    /* full opaque */
  4567.     {
  4568.       for (b = 0; b < 3; b++)
  4569.     dest[b] = src[b];
  4570.     }
  4571.   else
  4572.     {
  4573.       if ((x % (GIMP_CHECK_SIZE) < GIMP_CHECK_SIZE_SM) ^
  4574.       (y % (GIMP_CHECK_SIZE) < GIMP_CHECK_SIZE_SM))
  4575.     check = GIMP_CHECK_LIGHT * 255;
  4576.       else
  4577.     check = GIMP_CHECK_DARK * 255;
  4578.  
  4579.       if (src_a == 0)    /* full transparent */
  4580.     {
  4581.       for (b = 0; b < 3; b++)
  4582.         dest[b] = check;
  4583.     }
  4584.       else
  4585.     {
  4586.       for (b = 0; b < 3; b++)
  4587.         dest[b] = (src[b] * src_a + check * (OPAQUE-src_a)) / OPAQUE;
  4588.     }
  4589.     }
  4590. }
  4591.  
  4592. /*************************************************************************/
  4593. /**                                    **/
  4594. /**            +++ Gradient Menu                **/
  4595. /**            +++ gm                        **/
  4596. /**                                    **/
  4597. /*************************************************************************/
  4598.  
  4599. void
  4600. gradient_menu_init ()
  4601. {
  4602.   gm_gradient_get_list ();
  4603.   gradient_menus = NULL;
  4604. }
  4605.  
  4606. void
  4607. gradient_menu_rescan ()
  4608. {
  4609.   GList        *tmp;
  4610.   GradientMenu    *gm;
  4611.   GtkWidget    *menu;
  4612.  
  4613.   /* Detach and destroy menus first */
  4614.   tmp = gradient_menus;
  4615.   while (tmp)
  4616.     {
  4617.       gm  = tmp->data;
  4618.       tmp = tmp->next;
  4619.       menu = GTK_MULTI_OPTION_MENU (gm->option_menu)->menu;
  4620.       if (menu)
  4621.     {
  4622.       gtk_multi_option_menu_remove_menu (GTK_MULTI_OPTION_MENU (gm->option_menu));
  4623.     }
  4624.     }
  4625.  
  4626.   /* reget list of gradient names */
  4627.   gm_gradient_get_list ();
  4628.  
  4629.   /* Create menus and attach them again */
  4630.   tmp = gradient_menus;
  4631.   while (tmp)
  4632.     {
  4633.       GtkWidget *parent;
  4634.  
  4635.       gm  = tmp->data;
  4636.       tmp = tmp->next;
  4637.       /* @GRADIENT_NAME */
  4638.       menu = gm_menu_new (gm, gm->gradient_name);
  4639.  
  4640.       /*
  4641.     FIXME:
  4642.     This is a kind of hack so that it doesn't mess up gtknotebook
  4643.     to set menu into an option menu which is not shown (but
  4644.     GTK_WIDGET_VISIBLE)
  4645.        */
  4646.       parent = gm->option_menu->parent;
  4647.       if (0 && (parent != NULL) && GTK_CHECK_TYPE (parent, gtk_container_get_type ()))
  4648.     {
  4649.         /*gtk_container_block_resize (GTK_CONTAINER (parent));*/
  4650.         gtk_multi_option_menu_set_menu (GTK_MULTI_OPTION_MENU (gm->option_menu), menu);
  4651.         /*gtk_container_unblock_resize (GTK_CONTAINER (parent));*/
  4652.     }
  4653.       else
  4654.         gtk_multi_option_menu_set_menu (GTK_MULTI_OPTION_MENU (gm->option_menu), menu);
  4655.     }
  4656. }
  4657.  
  4658. GradientMenu *
  4659. gradient_menu_new (GradientMenuCallback callback,
  4660.            gpointer            callback_data,
  4661.            gchar            *default_gradient_name)
  4662. {
  4663.   GtkWidget    *menu;
  4664.   GradientMenu    *gm;
  4665.  
  4666.   gm = g_new (GradientMenu, 1);
  4667.  
  4668.   gm->callback = callback;
  4669.   gm->callback_data = callback_data;
  4670.  
  4671.   gm->preview = gtk_preview_new (GTK_PREVIEW_COLOR);
  4672.   gtk_preview_size(GTK_PREVIEW (gm->preview),
  4673.             GM_PREVIEW_WIDTH,
  4674.             GM_PREVIEW_HEIGHT);
  4675.  
  4676.   gm->option_menu = gtk_multi_option_menu_new ();
  4677.  
  4678.   /* @GRADIENT_NAME */
  4679.   menu = gm_menu_new (gm, default_gradient_name);
  4680.   gtk_multi_option_menu_set_menu (GTK_MULTI_OPTION_MENU (gm->option_menu), menu);
  4681.  
  4682.   gtk_widget_show (gm->preview);
  4683.   gtk_widget_show (gm->option_menu);
  4684.  
  4685.   gradient_menus = g_list_append (gradient_menus, gm);
  4686.   gtk_signal_connect (GTK_OBJECT (gm->option_menu), "destroy",
  4687.               (GtkSignalFunc) &gm_option_menu_destroy_callback, gm);
  4688.  
  4689.   return gm;
  4690. }
  4691.  
  4692. /*
  4693. static void
  4694. gradient_menu_destroy (GradientMenu *gm)
  4695. {
  4696.   gtk_widget_destroy (gm->preview);
  4697.   gtk_widget_destroy (gm->option_menu);
  4698.  
  4699.   g_free (gm);
  4700. }
  4701. */
  4702.  
  4703. /* Local Functions */
  4704.  
  4705. static void
  4706. gm_gradient_get_list (void)
  4707. {
  4708.   int    i;
  4709.  
  4710.   if (gradient_names)
  4711.     {
  4712.       for (i = 0; i < num_gradient_names; i++)
  4713.     g_free (gradient_names[i]);
  4714.       g_free (gradient_names);
  4715.     }
  4716.  
  4717.   gradient_cache_flush ();    /* to make sure */
  4718.   gradient_names = gradient_get_list (&num_gradient_names);
  4719. }
  4720.  
  4721. /*
  4722.  *  Create GtkMenu and arrange GtkMenuItem's of gradient names into it
  4723.  */
  4724.  
  4725. static GtkWidget *
  4726. gm_menu_new (GradientMenu *gm,
  4727.          gchar        *default_gradient_name)
  4728. {
  4729.   GtkWidget    *menu;
  4730.   GtkWidget    *menuitem;
  4731.   gchar        *active_name;
  4732.  
  4733.   menu = gtk_menu_new ();
  4734.  
  4735.   if (num_gradient_names == 0)
  4736.     {
  4737.       menuitem = gtk_menu_item_new_with_label (_("none"));
  4738.       gtk_widget_set_sensitive (menuitem, FALSE);
  4739.       gtk_widget_show (menuitem);
  4740.       gtk_menu_append (GTK_MENU (menu), menuitem);
  4741.       gtk_menu_set_active (GTK_MENU (menu), 0);
  4742.     }
  4743.   else /* num_gradient_names == 0 */
  4744.     {
  4745.       menu = gm_menu_create_sub_menus (gm, 0, &active_name,
  4746.                        default_gradient_name);
  4747.       if (active_name == NULL)
  4748.     {
  4749.       active_name = gradient_names[0];
  4750.       g_warning (_("Not found \"%s\": used \"%s\" instead"),
  4751.              default_gradient_name, active_name);
  4752.     }
  4753.  
  4754.       gradient_name_copy (gm->gradient_name, active_name);
  4755.       gm_preview_draw (gm->preview, active_name);
  4756.       if (GTK_WIDGET_VISIBLE (gm->preview) && GTK_WIDGET_MAPPED(gm->preview))
  4757.     {
  4758.       DEBUG_PRINT (("gm_menu_new: preview is visible and mapped\n"));
  4759.       gtk_widget_draw (gm->preview, NULL);
  4760.     }
  4761.       if (gm->callback)
  4762.     (* gm->callback) (active_name, gm->callback_data);
  4763.  
  4764.     } /* num_gradient_names == 0 */
  4765.  
  4766.   return menu;
  4767. }
  4768.  
  4769. static GtkWidget *
  4770. gm_menu_create_sub_menus (GradientMenu  *gm,
  4771.               gint           start_n,
  4772.               gchar        **active_name_ptr,
  4773.               gchar         *default_gradient_name)
  4774. {
  4775.   GtkWidget *menu, *sub_menu;
  4776.   gchar     *sub_active_name;
  4777.   GtkWidget *menuitem;
  4778.   gchar     *name;
  4779.   gint       active_i = 0;
  4780.   gint       i, n;
  4781.  
  4782.   *active_name_ptr = NULL;
  4783.   if (start_n >= num_gradient_names)
  4784.     {
  4785.       return NULL;
  4786.     }
  4787.  
  4788.   /* gradient_names[] are malloced strings and alive during
  4789.      this menuitem lives */
  4790.  
  4791.   menu = gtk_menu_new ();
  4792.   for (i = 0, n = start_n; i < GM_MENU_MAX && n < num_gradient_names; i++, n++)
  4793.     {
  4794.       name = gradient_names[n];
  4795.       if (strcmp (name, default_gradient_name) == 0)
  4796.     {
  4797.       active_i = i;
  4798.       *active_name_ptr = name;
  4799.     }
  4800.       menuitem = gtk_menu_item_new_with_label (name);
  4801.       gtk_object_set_user_data (GTK_OBJECT (menuitem), gm);
  4802.       gtk_signal_connect (GTK_OBJECT (menuitem), "activate",
  4803.               GTK_SIGNAL_FUNC (gm_menu_item_callback),
  4804.               name);
  4805.       gtk_menu_append (GTK_MENU (menu), menuitem);
  4806.       gtk_widget_show (menuitem);
  4807.     } /* for */
  4808.  
  4809.   sub_menu = gm_menu_create_sub_menus (gm, n, &sub_active_name,
  4810.                        default_gradient_name);
  4811.   if (sub_menu)
  4812.     {
  4813.       active_i += 2;
  4814.  
  4815.       /* hline */
  4816.       menuitem = gtk_menu_item_new ();
  4817.       gtk_widget_set_sensitive (menuitem, FALSE);
  4818.       gtk_widget_show (menuitem);
  4819.       gtk_menu_prepend (GTK_MENU (menu), menuitem);
  4820.  
  4821.       menuitem = gtk_menu_item_new_with_label (_("More..."));
  4822.       gtk_widget_show (menuitem);
  4823.       gtk_menu_prepend (GTK_MENU (menu), menuitem);
  4824.       gtk_menu_item_set_submenu (GTK_MENU_ITEM (menuitem), sub_menu);
  4825.  
  4826.       if (sub_active_name)
  4827.     {
  4828.       *active_name_ptr = sub_active_name;
  4829.       active_i = 0; /* "More ..." */
  4830.     }
  4831.     }
  4832.  
  4833.   gtk_menu_set_active (GTK_MENU (menu), active_i);
  4834.   return menu;
  4835. }
  4836.  
  4837. static void
  4838. gm_menu_item_callback (GtkWidget *w,
  4839.                gpointer   data)
  4840. {
  4841.   GradientMenu *gm;
  4842.   gchar        *gradient_name = (gchar *) data;
  4843.  
  4844.   DEBUG_PRINT(("gm_menu_item_callback\n"));
  4845.  
  4846.   gm =    (GradientMenu *) gtk_object_get_user_data (GTK_OBJECT (w));
  4847.  
  4848.   gradient_name_copy (gm->gradient_name, gradient_name);
  4849.  
  4850.   gm_preview_draw (gm->preview, gradient_name);
  4851.   if (GTK_WIDGET_VISIBLE (gm->preview) && GTK_WIDGET_MAPPED(gm->preview))
  4852.     {
  4853.       DEBUG_PRINT(("gm_menu_item_callback: preview is visible and mapped\n"));
  4854.       gtk_widget_draw (gm->preview, NULL);
  4855.     }
  4856.  
  4857.   if (gm->callback)
  4858.     (* gm->callback) (gradient_name, gm->callback_data);
  4859. }
  4860.  
  4861. static void
  4862. gm_preview_draw (GtkWidget *preview,
  4863.          gchar     *gradient_name)
  4864. {
  4865.   guchar      values[GM_PREVIEW_WIDTH][4];
  4866.   gint        nvalues = GM_PREVIEW_WIDTH;
  4867.   gint        row, irow, col;
  4868.   guchar      dest_row[GM_PREVIEW_WIDTH][3];
  4869.   guchar     *dest;
  4870.   guchar     *src;
  4871.   gint        check, b;
  4872.   const gint  alpha = 3;
  4873.  
  4874.   gradient_get_values (gradient_name, (guchar *)values, nvalues);
  4875.  
  4876.   for (row = 0; row < GM_PREVIEW_HEIGHT; row += GIMP_CHECK_SIZE_SM)
  4877.     {
  4878.       for (col = 0; col < GM_PREVIEW_WIDTH; col++)
  4879.     {
  4880.       dest = dest_row[col];
  4881.       src  = values[col];
  4882.  
  4883.       if (src[alpha] == OPAQUE)
  4884.         {
  4885.           /* no alpha channel or opaque -- simple way */
  4886.           for (b = 0; b < alpha; b++)
  4887.         dest[b] = src[b];
  4888.         }
  4889.       else
  4890.         {
  4891.           /* more or less transparent */
  4892.           if((col % (GIMP_CHECK_SIZE) < GIMP_CHECK_SIZE_SM) ^
  4893.           (row % (GIMP_CHECK_SIZE) < GIMP_CHECK_SIZE_SM))
  4894.         check = GIMP_CHECK_LIGHT * 255;
  4895.           else
  4896.         check = GIMP_CHECK_DARK * 255;
  4897.  
  4898.           if (src[alpha] == 0)
  4899.         {
  4900.           /* full transparent -- check */
  4901.           for (b = 0; b < alpha; b++)
  4902.             dest[b] = check;
  4903.         }
  4904.           else
  4905.         {
  4906.           /* middlemost transparent -- mix check and src */
  4907.           for (b = 0; b < alpha; b++)
  4908.             dest[b] = (src[b]*src[alpha] + check*(OPAQUE-src[alpha])) / OPAQUE;
  4909.         }
  4910.         }
  4911.     }
  4912.       for(irow = 0; irow < GIMP_CHECK_SIZE_SM && row + irow < GM_PREVIEW_HEIGHT; irow++)
  4913.     {
  4914.       gtk_preview_draw_row(GTK_PREVIEW (preview), (guchar*) dest_row,
  4915.                 0, row + irow, GM_PREVIEW_WIDTH);
  4916.     }
  4917.     }
  4918.  
  4919. }
  4920.  
  4921. static void
  4922. gm_option_menu_destroy_callback (GtkWidget *w,
  4923.                  gpointer   data)
  4924. {
  4925.   GradientMenu *gm = data;
  4926.   gradient_menus = g_list_remove (gradient_menus, gm);
  4927. }
  4928.  
  4929. /*************************************************************************/
  4930. /**                                    **/
  4931. /**            +++ Gradients                    **/
  4932. /**                                    **/
  4933. /*************************************************************************/
  4934.  
  4935. /*
  4936.     Manage both internal and external gradients: list up, cache,
  4937.     sampling, etc.
  4938.  
  4939.     External gradients are cached.
  4940.  */
  4941.  
  4942.  
  4943. void
  4944. gradient_name_copy (gchar *dest,
  4945.             gchar *src)
  4946. {
  4947.   strncpy (dest, src, GRADIENT_NAME_MAX);
  4948.   dest[GRADIENT_NAME_MAX-1] = '\0';
  4949. }
  4950.  
  4951. /*
  4952.   Translate SPACE to "\\040", etc.
  4953.  */
  4954. void
  4955. gradient_name_encode (guchar *dest,
  4956.               guchar *src)
  4957. {
  4958.   gint cnt = GRADIENT_NAME_MAX - 1;
  4959.  
  4960.   while (*src && cnt--)
  4961.     {
  4962.       if (iscntrl (*src) || isspace (*src) || *src == '\\')
  4963.     {
  4964.       sprintf (dest, "\\%03o", *src++);
  4965.       dest += 4;
  4966.     }
  4967.       else
  4968.     *dest++ = *src++;
  4969.     }
  4970.   *dest = '\0';
  4971. }
  4972.  
  4973. /*
  4974.   Translate "\\040" to SPACE, etc.
  4975.  */
  4976. void
  4977. gradient_name_decode (guchar *dest,
  4978.               guchar *src)
  4979. {
  4980.   gint cnt = GRADIENT_NAME_MAX - 1;
  4981.   gint tmp;
  4982.  
  4983.   while (*src && cnt--)
  4984.     {
  4985.       if (*src == '\\' && *(src+1) && *(src+2) && *(src+3))
  4986.     {
  4987.       sscanf (src+1, "%3o", &tmp);
  4988.       *dest++ = tmp;
  4989.       src += 4;
  4990.     }
  4991.       else
  4992.     *dest++ = *src++;
  4993.     }
  4994.   *dest = '\0';
  4995. }
  4996.  
  4997.  
  4998. void
  4999. gradient_init (void)
  5000. {
  5001.   gradient_cache_head = NULL;
  5002.   gradient_cache_count = 0;
  5003. }
  5004.  
  5005. void
  5006. gradient_free (void)
  5007. {
  5008.   gradient_cache_flush ();
  5009. }
  5010.  
  5011. gchar **
  5012. gradient_get_list (gint *num_gradients)
  5013. {
  5014.   gchar **gradients;
  5015.   gchar    **external_gradients = NULL;
  5016.   gint      external_ngradients = 0;
  5017.   gint      i, n;
  5018.  
  5019.   gradient_cache_flush ();
  5020.   external_gradients = gimp_gradients_get_list (&external_ngradients);
  5021.  
  5022.   *num_gradients = internal_ngradients + external_ngradients;
  5023.   gradients = g_new (gchar *, *num_gradients);
  5024.  
  5025.   n = 0;
  5026.   for (i = 0; i < internal_ngradients; i++)
  5027.     {
  5028.       gradients[n++] = g_strdup (internal_gradients[i]);
  5029.     }
  5030.   for (i = 0; i < external_ngradients; i++)
  5031.     {
  5032.       gradients[n++] = g_strdup (external_gradients[i]);
  5033.     }
  5034.  
  5035.   return gradients;
  5036. }
  5037.  
  5038. void
  5039. gradient_get_values (gchar  *gradient_name,
  5040.              guchar *values,
  5041.              gint    nvalues)
  5042. {
  5043.   /* DEBUG_PRINT (("gradient_get_values: %s %d\n", gradient_name, nvalues)); */
  5044.  
  5045.   /*
  5046.     Criteria to distinguish internal and external is rather simple here.
  5047.     It should be fixed later.
  5048.    */
  5049.   if (gradient_name[0] == '%')
  5050.     gradient_get_values_internal (gradient_name, values, nvalues);
  5051.   else
  5052.     gradient_get_values_external (gradient_name, values, nvalues);
  5053. }
  5054.  
  5055. static void
  5056. gradient_get_values_internal (gchar  *gradient_name,
  5057.                   guchar *values,
  5058.                   gint    nvalues)
  5059. {
  5060.   static guchar white[4]        = { 255, 255, 255, 255 };
  5061.   static guchar white_trans[4]  = { 255, 255, 255, 0   };
  5062.   static guchar red_trans[4]    = { 255, 0,   0,   0   };
  5063.   static guchar blue_trans[4]   = { 0,   0,   255, 0   };
  5064.   static guchar yellow_trans[4] = { 255, 255, 0,   0   };
  5065.  
  5066.   /*
  5067.     The internal gradients here are example --
  5068.     What kind of internals would be useful ?
  5069.    */
  5070.   if(!strcmp(gradient_name, "%white"))
  5071.     {
  5072.       gradient_get_blend (white, white, values, nvalues);
  5073.     }
  5074.   else if(!strcmp(gradient_name, "%white_grad"))
  5075.     {
  5076.       gradient_get_blend (white, white_trans, values, nvalues);
  5077.     }
  5078.   else if (!strcmp (gradient_name, "%red_grad"))
  5079.     {
  5080.       gradient_get_blend (white, red_trans, values, nvalues);
  5081.     }
  5082.   else if (!strcmp (gradient_name, "%blue_grad"))
  5083.     {
  5084.       gradient_get_blend (white, blue_trans, values, nvalues);
  5085.     }
  5086.   else if (!strcmp (gradient_name, "%yellow_grad"))
  5087.     {
  5088.       gradient_get_blend (white, yellow_trans, values, nvalues);
  5089.     }
  5090.   else if (!strcmp (gradient_name, "%random"))
  5091.     {
  5092.       gradient_get_random (1, values, nvalues);
  5093.     }
  5094.   else
  5095.     {
  5096.       gradient_get_default (gradient_name, values, nvalues);
  5097.     }
  5098. }
  5099.  
  5100. static void
  5101. gradient_get_blend (guchar *fg,
  5102.             guchar *bg,
  5103.             guchar *values,
  5104.             gint    nvalues)
  5105. {
  5106.   gdouble  x;
  5107.   gint     i;
  5108.   gint     j;
  5109.   guchar  *v = values;
  5110.  
  5111.   for (i=0; i<nvalues; i++)
  5112.     {
  5113.       x = (double) i / nvalues;
  5114.       for (j = 0; j < 4; j++)
  5115.     *v++ = fg[j] * (1 - x) + bg[j] * x;
  5116.     }
  5117. }
  5118.  
  5119. static void
  5120. gradient_get_random (gint    seed,
  5121.              guchar *values,
  5122.              gint    nvalues)
  5123. {
  5124.   gint    i;
  5125.   gint    j;
  5126.   gint    inten;
  5127.   guchar *v = values;
  5128.  
  5129.   /*
  5130.     This is really simple  -- gaussian noise might be better
  5131.    */
  5132.   srand (seed);
  5133.   for (i = 0; i < nvalues; i++)
  5134.     {
  5135.       inten = rand () % 256;
  5136.       for (j = 0; j < 3; j++)
  5137.     *v++ = inten;
  5138.       *v++ = 255;
  5139.     }
  5140. }
  5141.  
  5142. static void
  5143. gradient_get_default (gchar  *name,
  5144.               guchar *values,
  5145.               gint    nvalues)
  5146. {
  5147.   gdouble  e[3];
  5148.   gdouble  x;
  5149.   gint     i;
  5150.   gint     j;
  5151.   guchar  *v = values;
  5152.  
  5153.   /*
  5154.     Create gradient by name
  5155.    */
  5156.   name++;
  5157.   for (j = 0; j < 3; j++)
  5158.     e[j] = name[j] / 255.0;
  5159.  
  5160.   for (i = 0; i < nvalues; i++)
  5161.     {
  5162.       x = (double) i / nvalues;
  5163.       for (j = 0; j < 3; j++)
  5164.     *v++ = 255 * pow (x, e[j]);
  5165.       *v++ = 255;
  5166.     }
  5167. }
  5168.  
  5169. /*
  5170.   Caching gradients is really needed. It really takes 0.2 seconds each
  5171.   time to resample an external gradient. (And this plug-in has
  5172.   currently 6 gradient menus.)
  5173.  
  5174.   However, this caching routine is not too good. It picks up just
  5175.   GRADIENT_RESOLUTION samples everytime, and rescales it later.     And
  5176.   cached values are stored in guchar array. No accuracy.
  5177.  */
  5178. static void
  5179. gradient_get_values_external (gchar  *gradient_name,
  5180.                   guchar *values,
  5181.                   gint    nvalues)
  5182. {
  5183.   GradientCacheItem *ci;
  5184.   gint             found;
  5185. #ifdef DEBUG
  5186.   clock_t    clk = clock ();
  5187. #endif
  5188.  
  5189.   g_return_if_fail (nvalues >= 2);
  5190.  
  5191.   ci = gradient_cache_lookup (gradient_name, &found);
  5192.   if (!found)
  5193.     {
  5194.       gradient_get_values_real_external (gradient_name, ci->values, GRADIENT_RESOLUTION);
  5195.     }
  5196.   if (nvalues == GRADIENT_RESOLUTION)
  5197.     {
  5198.       memcpy (values, ci->values, 4 * GRADIENT_RESOLUTION);
  5199.     }
  5200.   else
  5201.     {
  5202.       double    pos, frac;
  5203.       int    ipos;
  5204.       int    i, j;
  5205.  
  5206.       for (i = 0; i < nvalues; i++)
  5207.     {
  5208.       pos = ((double) i / (nvalues - 1)) * (GRADIENT_RESOLUTION - 1);
  5209.       g_assert (0 <= pos && pos <= GRADIENT_RESOLUTION - 1);
  5210.       ipos = (int) pos; frac = pos - ipos;
  5211.       if (frac == 0.0)
  5212.         {
  5213.           memcpy (&values[4 * i], &ci->values[4 * ipos], 4);
  5214.         }
  5215.       else
  5216.         for (j = 0; j < 4; j++)
  5217.           values[4 * i + j] = ci->values[4 * ipos + j] * (1 - frac)
  5218.                 + ci->values[4 * (ipos + 1) + j] * frac;
  5219.     }
  5220.     }
  5221.  
  5222. #ifdef DEBUG
  5223.   get_values_external_clock += clock () - clk;
  5224.   get_values_external_count ++;
  5225. #endif
  5226.  
  5227. }
  5228.  
  5229. static void
  5230. gradient_get_values_real_external (gchar  *gradient_name,
  5231.                    guchar *values,
  5232.                    gint    nvalues)
  5233. {
  5234.   gchar   *old_name;
  5235.   gdouble *tmp_values;
  5236.   gint     i;
  5237.   gint     j;
  5238.  
  5239.   old_name = gimp_gradients_get_active ();
  5240.  
  5241.   gimp_gradients_set_active (gradient_name);
  5242.  
  5243.   tmp_values = gimp_gradients_sample_uniform (nvalues);
  5244.   for (i = 0; i < nvalues; i++)
  5245.     for (j = 0; j < 4; j++)
  5246.       values[4*i+j] = (guchar) (tmp_values[4*i+j] * 255);
  5247.  
  5248.   gimp_gradients_set_active (old_name);
  5249.  
  5250.   g_free (tmp_values);
  5251.   g_free (old_name);
  5252. }
  5253.  
  5254. void
  5255. gradient_cache_flush (void)
  5256. {
  5257.   GradientCacheItem *ci;
  5258.   GradientCacheItem *tmp;
  5259.  
  5260.   ci = gradient_cache_head;
  5261.   while (ci)
  5262.     {
  5263.       tmp = ci->next;
  5264.       g_free (ci);
  5265.       ci = tmp;
  5266.     }
  5267.   gradient_cache_head = NULL;
  5268.   gradient_cache_count = 0;
  5269. }
  5270.  
  5271. static GradientCacheItem *
  5272. gradient_cache_lookup (gchar *name,
  5273.                gint  *found)
  5274. {
  5275.   GradientCacheItem *ci;
  5276.  
  5277.   ci = gradient_cache_head;
  5278.   while (ci)
  5279.     {
  5280.       if (!strcmp (ci->name, name))
  5281.     break;
  5282.       ci = ci->next;
  5283.     }
  5284.   if (ci)
  5285.     {
  5286.       *found = TRUE;
  5287.       if (!ci->prev)
  5288.     {
  5289.       g_assert (ci == gradient_cache_head);
  5290.       return ci;
  5291.     }
  5292.       ci->prev->next = ci->next;
  5293.       if (ci->next)
  5294.     ci->next->prev = ci->prev;
  5295.       ci->next = gradient_cache_head;
  5296.       gradient_cache_head->prev = ci;
  5297.       gradient_cache_head = ci;
  5298.       ci->prev = NULL;
  5299.       return ci;
  5300.     }
  5301.   else
  5302.     {
  5303.       *found = FALSE;
  5304.       while (gradient_cache_count >= GRADIENT_CACHE_SIZE)
  5305.     gradient_cache_zorch();
  5306.       ci = g_new (GradientCacheItem, 1);
  5307.       strncpy (ci->name, name, GRADIENT_NAME_MAX - 1);
  5308.       ci->next = gradient_cache_head;
  5309.       ci->prev = NULL;
  5310.       if (gradient_cache_head)
  5311.     gradient_cache_head->prev = ci;
  5312.       gradient_cache_head = ci;
  5313.       ++gradient_cache_count;
  5314.       return ci;
  5315.     }
  5316. }
  5317.  
  5318. static void
  5319. gradient_cache_zorch (void)
  5320. {
  5321.   GradientCacheItem *ci = gradient_cache_head;
  5322.  
  5323.   while (ci && ci->next)
  5324.     {
  5325.       ci = ci->next;
  5326.     }
  5327.   if (ci)
  5328.     {
  5329.       g_assert (ci->next == NULL);
  5330.       if (ci->prev)
  5331.     ci->prev->next = NULL;
  5332.       else
  5333.     gradient_cache_head = NULL;
  5334.       g_free (ci);
  5335.       --gradient_cache_count;
  5336.     }
  5337. }
  5338.  
  5339. #ifdef DEBUG
  5340. void
  5341. gradient_report (void)
  5342. {
  5343.   double total = (double) get_values_external_clock / CLOCKS_PER_SEC;
  5344.  
  5345.   printf("gradient_get_values_external %.2f sec. / %d times (ave %.2f sec.)\n",
  5346.       total,
  5347.       get_values_external_count,
  5348.       total / get_values_external_count);
  5349. }
  5350. #endif
  5351.