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