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