mirror of
https://gitlab.gnome.org/GNOME/gimp.git
synced 2025-07-03 09:23:24 +00:00

As Lloyd Konneker noted, the calc.sflare_list was not being accessed at the right location when the Edit Dialog preview of the gradient was updated. The API page for g_list_free_full () recommends using g_steal_pointer () to ensure the head element is not left dangling. This aligns with other usage in the GIMP codebase and seems to stop the crash. Additional safety checks were also added.
5171 lines
155 KiB
C
5171 lines
155 KiB
C
/* GIMP - The GNU Image Manipulation Program
|
||
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
|
||
*
|
||
* GFlare plug-in -- lense flare effect by using custom gradients
|
||
* Copyright (C) 1997 Eiichi Takamori <taka@ma1.sekyou.ne.jp>
|
||
*
|
||
* This program is free software: you can redistribute it and/or modify
|
||
* it under the terms of the GNU General Public License as published by
|
||
* the Free Software Foundation; either version 3 of the License, or
|
||
* (at your option) any later version.
|
||
*
|
||
* This program is distributed in the hope that it will be useful,
|
||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
* GNU General Public License for more details.
|
||
*
|
||
* You should have received a copy of the GNU General Public License
|
||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||
*
|
||
*
|
||
* A fair proportion of this code was taken from GIMP & Script-fu
|
||
* copyrighted by Spencer Kimball and Peter Mattis, and from Gradient
|
||
* Editor copyrighted by Federico Mena Quintero. (See copyright notice
|
||
* below) Thanks for senior GIMP hackers!!
|
||
*
|
||
* GIMP - The GNU Image Manipulation Program
|
||
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
|
||
*
|
||
* Gradient editor module copyight (C) 1996-1997 Federico Mena Quintero
|
||
* federico@nuclecu.unam.mx
|
||
*/
|
||
|
||
#include "config.h"
|
||
|
||
#include <string.h>
|
||
#include <stdlib.h>
|
||
#include <errno.h>
|
||
|
||
#ifdef HAVE_UNISTD_H
|
||
#include <unistd.h>
|
||
#endif
|
||
|
||
#include <glib/gstdio.h>
|
||
|
||
#include <glib-object.h>
|
||
|
||
#include <libgimp/gimp.h>
|
||
#include <libgimp/gimpui.h>
|
||
|
||
#include "libgimp/stdplugins-intl.h"
|
||
|
||
/* #define DEBUG */
|
||
|
||
#ifdef DEBUG
|
||
#define DEBUG_PRINT(X) g_print X
|
||
#else
|
||
#define DEBUG_PRINT(X)
|
||
#endif
|
||
|
||
#define LUMINOSITY(PIX) (GIMP_RGB_LUMINANCE (PIX[0], PIX[1], PIX[2]) + 0.5)
|
||
|
||
#define RESPONSE_RESCAN 1
|
||
|
||
#define PLUG_IN_PROC "plug-in-gflare"
|
||
#define PLUG_IN_BINARY "gradient-flare"
|
||
#define PLUG_IN_ROLE "gimp-gradient-flare"
|
||
|
||
#define GRADIENT_NAME_MAX 256
|
||
#define GRADIENT_RESOLUTION 360
|
||
|
||
#define GFLARE_NAME_MAX 256
|
||
#define GFLARE_FILE_HEADER "GIMP GFlare 0.25\n"
|
||
#define SFLARE_NUM 30
|
||
|
||
#define DLG_PREVIEW_WIDTH 256
|
||
#define DLG_PREVIEW_HEIGHT 256
|
||
#define DLG_PREVIEW_MASK GDK_EXPOSURE_MASK | \
|
||
GDK_BUTTON_PRESS_MASK
|
||
#define DLG_LISTBOX_WIDTH 80
|
||
#define DLG_LISTBOX_HEIGHT 40
|
||
|
||
#define ED_PREVIEW_WIDTH 256
|
||
#define ED_PREVIEW_HEIGHT 256
|
||
|
||
#define GM_PREVIEW_WIDTH 80
|
||
#define GM_PREVIEW_HEIGHT 16
|
||
|
||
#ifndef OPAQUE
|
||
#define OPAQUE 255
|
||
#endif
|
||
#define GRAY50 128
|
||
|
||
#define GRADIENT_CACHE_SIZE 32
|
||
|
||
#define CALC_GLOW 0x01
|
||
#define CALC_RAYS 0x02
|
||
#define CALC_SFLARE 0x04
|
||
|
||
typedef struct _Preview Preview;
|
||
|
||
typedef gchar GradientName[GRADIENT_NAME_MAX];
|
||
|
||
typedef enum
|
||
{
|
||
GF_NORMAL = 0,
|
||
GF_ADDITION,
|
||
GF_OVERLAY,
|
||
GF_SCREEN,
|
||
GF_NUM_MODES
|
||
} GFlareMode;
|
||
|
||
typedef enum
|
||
{
|
||
GF_CIRCLE = 0,
|
||
GF_POLYGON,
|
||
GF_NUM_SHAPES
|
||
} GFlareShape;
|
||
|
||
typedef struct
|
||
{
|
||
gchar *name;
|
||
gchar *filename;
|
||
gdouble glow_opacity;
|
||
GFlareMode glow_mode;
|
||
gdouble rays_opacity;
|
||
GFlareMode rays_mode;
|
||
gdouble sflare_opacity;
|
||
GFlareMode sflare_mode;
|
||
GradientName glow_radial;
|
||
GradientName glow_angular;
|
||
GradientName glow_angular_size;
|
||
gdouble glow_size;
|
||
gdouble glow_rotation;
|
||
gdouble glow_hue;
|
||
GradientName rays_radial;
|
||
GradientName rays_angular;
|
||
GradientName rays_angular_size;
|
||
gdouble rays_size;
|
||
gdouble rays_rotation;
|
||
gdouble rays_hue;
|
||
gint rays_nspikes;
|
||
gdouble rays_thickness;
|
||
GradientName sflare_radial;
|
||
GradientName sflare_sizefac;
|
||
GradientName sflare_probability;
|
||
gdouble sflare_size;
|
||
gdouble sflare_rotation;
|
||
gdouble sflare_hue;
|
||
GFlareShape sflare_shape;
|
||
gint sflare_nverts;
|
||
guint32 sflare_seed;
|
||
gboolean random_seed;
|
||
} GFlare;
|
||
|
||
typedef struct
|
||
{
|
||
FILE *fp;
|
||
gint error;
|
||
} GFlareFile;
|
||
|
||
|
||
typedef enum
|
||
{
|
||
PAGE_SETTINGS,
|
||
PAGE_SELECTOR,
|
||
PAGE_GENERAL,
|
||
PAGE_GLOW,
|
||
PAGE_RAYS,
|
||
PAGE_SFLARE
|
||
} PageNum;
|
||
|
||
|
||
typedef struct
|
||
{
|
||
gint init;
|
||
GFlare *gflare;
|
||
GtkWidget *shell;
|
||
Preview *preview;
|
||
struct
|
||
{
|
||
gdouble x0, y0, x1, y1;
|
||
} pwin;
|
||
gboolean update_preview;
|
||
GtkWidget *notebook;
|
||
GtkWidget *sizeentry;
|
||
GtkWidget *asupsample_frame;
|
||
GtkListStore *selector_list;
|
||
GtkTreeSelection *selection;
|
||
gint init_params_done;
|
||
} GFlareDialog;
|
||
|
||
typedef void (* GFlareEditorCallback) (gint updated, gpointer data);
|
||
|
||
typedef struct
|
||
{
|
||
gint init;
|
||
gint run;
|
||
GFlareEditorCallback callback;
|
||
gpointer calldata;
|
||
GFlare *target_gflare;
|
||
GFlare *gflare;
|
||
GtkWidget *shell;
|
||
Preview *preview;
|
||
GtkWidget *notebook;
|
||
PageNum cur_page;
|
||
GtkWidget *polygon_entry;
|
||
GtkWidget *polygon_toggle;
|
||
gint init_params_done;
|
||
} GFlareEditor;
|
||
|
||
typedef struct
|
||
{
|
||
gdouble x0;
|
||
gdouble y0;
|
||
gdouble x1;
|
||
gdouble y1;
|
||
} CalcBounds;
|
||
|
||
typedef struct
|
||
{
|
||
gint init;
|
||
gint type;
|
||
GFlare *gflare;
|
||
gdouble xcenter;
|
||
gdouble ycenter;
|
||
gdouble radius;
|
||
gdouble rotation;
|
||
gdouble hue;
|
||
gdouble vangle;
|
||
gdouble vlength;
|
||
|
||
gint glow_opacity;
|
||
CalcBounds glow_bounds;
|
||
guchar *glow_radial;
|
||
guchar *glow_angular;
|
||
guchar *glow_angular_size;
|
||
gdouble glow_radius;
|
||
gdouble glow_rotation;
|
||
|
||
gint rays_opacity;
|
||
CalcBounds rays_bounds;
|
||
guchar *rays_radial;
|
||
guchar *rays_angular;
|
||
guchar *rays_angular_size;
|
||
gdouble rays_radius;
|
||
gdouble rays_rotation;
|
||
gdouble rays_spike_mod;
|
||
gdouble rays_thinness;
|
||
|
||
gint sflare_opacity;
|
||
GList *sflare_list;
|
||
guchar *sflare_radial;
|
||
guchar *sflare_sizefac;
|
||
guchar *sflare_probability;
|
||
gdouble sflare_radius;
|
||
gdouble sflare_rotation;
|
||
GFlareShape sflare_shape;
|
||
gdouble sflare_angle;
|
||
gdouble sflare_factor;
|
||
} CalcParams;
|
||
|
||
/*
|
||
* What's the difference between (structure) CalcParams and GFlare ?
|
||
* well, radius and lengths are actual length for CalcParams where
|
||
* they are typically 0 to 100 for GFlares, and angles are G_PI based
|
||
* (radian) for CalcParams where they are degree for GFlares. et cetra.
|
||
* This is because convenience for dialog processing and for calculating.
|
||
* these conversion is taken place in calc init routines. see below.
|
||
*/
|
||
|
||
typedef struct
|
||
{
|
||
gdouble xcenter;
|
||
gdouble ycenter;
|
||
gdouble radius;
|
||
CalcBounds bounds;
|
||
} CalcSFlare;
|
||
|
||
typedef struct
|
||
{
|
||
const Babl *format;
|
||
gint bpp;
|
||
gint is_color;
|
||
gint has_alpha;
|
||
gint x, y, w, h; /* mask bounds */
|
||
} DrawableInfo;
|
||
|
||
typedef struct _GradientMenu GradientMenu;
|
||
typedef void (* GradientMenuCallback) (const gchar *gradient_name,
|
||
gpointer data);
|
||
struct _GradientMenu
|
||
{
|
||
GtkWidget *preview;
|
||
GtkWidget *combo;
|
||
GradientMenuCallback callback;
|
||
gpointer callback_data;
|
||
GradientName gradient_name;
|
||
};
|
||
|
||
typedef gint (* PreviewInitFunc) (Preview *preview,
|
||
gpointer data);
|
||
typedef void (* PreviewRenderFunc) (Preview *preview,
|
||
guchar *buffer,
|
||
gint y,
|
||
gpointer data);
|
||
typedef void (* PreviewDeinitFunc) (Preview *preview,
|
||
gpointer data);
|
||
|
||
struct _Preview
|
||
{
|
||
GtkWidget *widget;
|
||
gint width;
|
||
gint height;
|
||
PreviewInitFunc init_func;
|
||
gpointer init_data;
|
||
PreviewRenderFunc render_func;
|
||
gpointer render_data;
|
||
PreviewDeinitFunc deinit_func;
|
||
gpointer deinit_data;
|
||
guint timeout_tag;
|
||
guint idle_tag;
|
||
gint init_done;
|
||
gint current_y;
|
||
gint drawn_y;
|
||
guchar *buffer;
|
||
guchar *full_image_buffer;
|
||
};
|
||
|
||
typedef struct
|
||
{
|
||
gint tag;
|
||
gint got_gradients;
|
||
gint current_y;
|
||
gint drawn_y;
|
||
PreviewRenderFunc render_func;
|
||
guchar *buffer;
|
||
} PreviewIdle;
|
||
|
||
typedef struct _GradientCacheItem GradientCacheItem;
|
||
|
||
struct _GradientCacheItem
|
||
{
|
||
GradientCacheItem *next;
|
||
GradientCacheItem *prev;
|
||
GradientName name;
|
||
guchar values[4 * GRADIENT_RESOLUTION];
|
||
};
|
||
|
||
|
||
typedef void (* QueryFunc) (GtkWidget *,
|
||
gpointer,
|
||
gpointer);
|
||
|
||
|
||
typedef struct _Gflare Gflare;
|
||
typedef struct _GflareClass GflareClass;
|
||
|
||
struct _Gflare
|
||
{
|
||
GimpPlugIn parent_instance;
|
||
};
|
||
|
||
struct _GflareClass
|
||
{
|
||
GimpPlugInClass parent_class;
|
||
};
|
||
|
||
|
||
#define GFLARE_TYPE (gflare_get_type ())
|
||
#define GFLARE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GFLARE_TYPE, Gflare))
|
||
|
||
GType gflare_get_type (void) G_GNUC_CONST;
|
||
|
||
static GList * gflare_query_procedures (GimpPlugIn *plug_in);
|
||
static GimpProcedure * gflare_create_procedure (GimpPlugIn *plug_in,
|
||
const gchar *name);
|
||
|
||
static GimpValueArray * gflare_run (GimpProcedure *procedure,
|
||
GimpRunMode run_mode,
|
||
GimpImage *image,
|
||
gint n_drawables,
|
||
GimpDrawable **drawables,
|
||
GimpProcedureConfig *config,
|
||
gpointer run_data);
|
||
|
||
static GFlare * gflare_new_with_default (const gchar *new_name);
|
||
static GFlare * gflare_dup (const GFlare *src,
|
||
const gchar *new_name);
|
||
static void gflare_copy (GFlare *dest,
|
||
const GFlare *src);
|
||
static GFlare * gflare_load (const gchar *filename,
|
||
const gchar *name);
|
||
static void gflare_save (GFlare *gflare);
|
||
|
||
static gint gflares_list_insert (GFlare *gflare);
|
||
static GFlare * gflares_list_lookup (const gchar *name);
|
||
static gint gflares_list_index (GFlare *gflare);
|
||
static gint gflares_list_remove (GFlare *gflare);
|
||
static void gflares_list_load_all (void);
|
||
static void gflares_list_free_all (void);
|
||
|
||
static void calc_init_params (GFlare *gflare,
|
||
gint calc_type,
|
||
gint xcenter,
|
||
gint ycenter,
|
||
gdouble radius,
|
||
gdouble rotation,
|
||
gdouble hue,
|
||
gdouble vangle,
|
||
gdouble vlength);
|
||
static gint calc_init_progress (void);
|
||
static void calc_deinit (void);
|
||
static void calc_glow_pix (guchar *dest_pix,
|
||
gdouble x,
|
||
gdouble y);
|
||
static void calc_rays_pix (guchar *dest_pix,
|
||
gdouble x,
|
||
gdouble y);
|
||
static void calc_sflare_pix (guchar *dest_pix,
|
||
gdouble x,
|
||
gdouble y,
|
||
guchar *src_pix);
|
||
static void calc_gflare_pix (guchar *dest_pix,
|
||
gdouble x,
|
||
gdouble y,
|
||
guchar *src_pix);
|
||
|
||
static gboolean dlg_run (GimpProcedure *procedure,
|
||
GimpProcedureConfig *config);
|
||
static void dlg_preview_calc_window (void);
|
||
static void ed_preview_calc_window (void);
|
||
static GtkWidget * ed_mode_menu_new (GFlareMode *mode_var);
|
||
|
||
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);
|
||
static void preview_free (Preview *preview);
|
||
static void preview_render_start (Preview *preview);
|
||
static void preview_render_end (Preview *preview);
|
||
static void preview_rgba_to_rgb (guchar *dest,
|
||
gint x,
|
||
gint y,
|
||
guchar *src);
|
||
|
||
static void gradient_menu_init (void);
|
||
static void gradient_menu_rescan (void);
|
||
static GradientMenu * gradient_menu_new (GradientMenuCallback callback,
|
||
gpointer callback_data,
|
||
const gchar *default_gradient_name);
|
||
static void gradient_name_copy (gchar *dest,
|
||
const gchar *src);
|
||
static void gradient_name_encode (gchar *dest,
|
||
const gchar *src);
|
||
static void gradient_name_decode (gchar *dest,
|
||
const gchar *src);
|
||
static void gradient_init (void);
|
||
static void gradient_free (void);
|
||
static gchar ** gradient_get_list (gint *num_gradients);
|
||
static void gradient_get_values (const gchar *gradient_name,
|
||
guchar *values,
|
||
gint nvalues);
|
||
static void gradient_cache_flush (void);
|
||
|
||
/**
|
||
*** +++ Static Functions Prototypes
|
||
**/
|
||
|
||
static void plugin_do (GimpProcedureConfig *config);
|
||
|
||
static void plugin_do_non_asupsample (GeglBuffer *src_buffer,
|
||
GeglBuffer *dest_buffer);
|
||
static void plugin_do_asupsample (GeglBuffer *src_buffer,
|
||
GeglBuffer *dest_buffer,
|
||
gint asupsample_max_depth,
|
||
gdouble asupsample_threshold);
|
||
static void plugin_render_func (gdouble x,
|
||
gdouble y,
|
||
GimpRGB *color,
|
||
gpointer data);
|
||
static void plugin_put_pixel_func (gint ix,
|
||
gint iy,
|
||
GimpRGB *color,
|
||
gpointer data);
|
||
static void plugin_progress_func (gint y1,
|
||
gint y2,
|
||
gint curr_y,
|
||
gpointer data);
|
||
|
||
static GFlare * gflare_new (void);
|
||
static void gflare_free (GFlare *gflare);
|
||
static void gflare_read_int (gint *intvar,
|
||
GFlareFile *gf);
|
||
static void gflare_read_double (gdouble *dblvar,
|
||
GFlareFile *gf);
|
||
static void gflare_read_gradient_name (GradientName name,
|
||
GFlareFile *gf);
|
||
static void gflare_read_shape (GFlareShape *shape,
|
||
GFlareFile *gf);
|
||
static void gflare_read_mode (GFlareMode *mode,
|
||
GFlareFile *gf);
|
||
static void gflare_write_gradient_name (GradientName name,
|
||
FILE *fp);
|
||
|
||
static gint calc_sample_one_gradient (void);
|
||
static void calc_place_sflare (void);
|
||
static void calc_get_gradient (guchar *pix,
|
||
guchar *gradient,
|
||
gdouble pos);
|
||
static gdouble fmod_positive (gdouble x,
|
||
gdouble m);
|
||
static void calc_paint_func (guchar *dest,
|
||
guchar *src1,
|
||
guchar *src2,
|
||
gint opacity,
|
||
GFlareMode mode);
|
||
static void calc_combine (guchar *dest,
|
||
guchar *src1,
|
||
guchar *src2,
|
||
gint opacity);
|
||
static void calc_addition (guchar *dest,
|
||
guchar *src1,
|
||
guchar *src2);
|
||
static void calc_screen (guchar *dest,
|
||
guchar *src1,
|
||
guchar *src2);
|
||
static void calc_overlay (guchar *dest,
|
||
guchar *src1,
|
||
guchar *src2);
|
||
|
||
static void dlg_setup_gflare (const gchar *gflare_name);
|
||
static void dlg_preview_realize (GtkWidget *widget);
|
||
static gboolean dlg_preview_handle_event (GtkWidget *widget,
|
||
GdkEvent *event,
|
||
GimpProcedureConfig *config);
|
||
static void dlg_preview_update (void);
|
||
static gint dlg_preview_init_func (Preview *preview,
|
||
gpointer data);
|
||
static void dlg_preview_render_func (Preview *preview,
|
||
guchar *dest,
|
||
gint y,
|
||
gpointer data);
|
||
static void dlg_preview_deinit_func (Preview *preview,
|
||
gpointer data);
|
||
static void dlg_make_page_settings (GFlareDialog *dlg,
|
||
GtkWidget *notebook,
|
||
GimpProcedureConfig *config);
|
||
static void dlg_position_entry_callback (GtkWidget *widget,
|
||
GimpProcedureConfig *config);
|
||
static void dlg_update_preview_callback (GtkWidget *widget,
|
||
gpointer data);
|
||
static void dlg_make_page_selector (GFlareDialog *dlg,
|
||
GtkWidget *notebook,
|
||
GimpProcedureConfig *config);
|
||
|
||
static void dlg_selector_setup_listbox (void);
|
||
static void dlg_selector_list_item_callback (GtkTreeSelection *selection);
|
||
|
||
static void dlg_selector_new_callback (GtkWidget *widget,
|
||
gpointer data);
|
||
static void dlg_selector_new_ok_callback (GtkWidget *widget,
|
||
const gchar *new_name,
|
||
gpointer data);
|
||
|
||
static void dlg_selector_edit_callback (GtkWidget *widget,
|
||
gpointer data);
|
||
static void dlg_selector_edit_done_callback (gint updated,
|
||
gpointer data);
|
||
|
||
static void dlg_selector_copy_callback (GtkWidget *widget,
|
||
gpointer data);
|
||
static void dlg_selector_copy_ok_callback (GtkWidget *widget,
|
||
const gchar *copy_name,
|
||
gpointer data);
|
||
|
||
static void dlg_selector_delete_callback (GtkWidget *widget,
|
||
gpointer data);
|
||
static void dlg_selector_do_delete_callback (GtkWidget *widget,
|
||
gboolean delete,
|
||
gpointer data);
|
||
|
||
static void ed_run (GtkWindow *parent,
|
||
GFlare *target_gflare,
|
||
GFlareEditorCallback callback,
|
||
gpointer calldata);
|
||
static void ed_destroy_callback (GtkWidget *widget,
|
||
GFlareEditor *ed);
|
||
static void ed_response (GtkWidget *widget,
|
||
gint response_id,
|
||
GFlareEditor *ed);
|
||
static void ed_make_page_general (GFlareEditor *ed,
|
||
GtkWidget *notebook);
|
||
static void ed_make_page_glow (GFlareEditor *ed,
|
||
GtkWidget *notebook);
|
||
static void ed_make_page_rays (GFlareEditor *ed,
|
||
GtkWidget *notebook);
|
||
static void ed_make_page_sflare (GFlareEditor *ed,
|
||
GtkWidget *notebook);
|
||
static void ed_put_gradient_menu (GtkWidget *grid,
|
||
gint x,
|
||
gint y,
|
||
const gchar *caption,
|
||
GradientMenu *gm);
|
||
static void ed_mode_menu_callback (GtkWidget *widget,
|
||
gpointer data);
|
||
static void ed_gradient_menu_callback (const gchar *gradient_name,
|
||
gpointer data);
|
||
static void ed_shape_radio_callback (GtkWidget *widget,
|
||
gpointer data);
|
||
static void ed_ientry_callback (GtkWidget *widget,
|
||
gpointer data);
|
||
static void ed_page_map_callback (GtkWidget *widget,
|
||
gpointer data);
|
||
static void ed_preview_update (void);
|
||
static gint ed_preview_init_func (Preview *preview,
|
||
gpointer data);
|
||
static void ed_preview_deinit_func (Preview *preview,
|
||
gpointer data);
|
||
static void ed_preview_render_func (Preview *preview,
|
||
guchar *buffer,
|
||
gint y,
|
||
gpointer data);
|
||
static void ed_preview_render_general (guchar *buffer,
|
||
gint y);
|
||
static void ed_preview_render_glow (guchar *buffer,
|
||
gint y);
|
||
static void ed_preview_render_rays (guchar *buffer,
|
||
gint y);
|
||
static void ed_preview_render_sflare (guchar *buffer,
|
||
gint y);
|
||
|
||
static gint preview_render_start_2 (Preview *preview);
|
||
static gint preview_handle_idle (Preview *preview);
|
||
|
||
static void gm_gradient_get_list (void);
|
||
static void gm_gradient_combo_fill (GradientMenu *gm,
|
||
const gchar *default_gradient);
|
||
static void gm_gradient_combo_callback (GtkWidget *widget,
|
||
gpointer data);
|
||
static void gm_preview_draw (GtkWidget *preview,
|
||
const gchar *gradient_name);
|
||
static void gm_combo_destroy_callback (GtkWidget *widget,
|
||
gpointer data);
|
||
|
||
static void gradient_get_values_internal (const gchar *gradient_name,
|
||
guchar *values,
|
||
gint nvalues);
|
||
static void gradient_get_blend (const guchar *fg,
|
||
const guchar *bg,
|
||
guchar *values,
|
||
gint nvalues);
|
||
static void gradient_get_random (guchar *values,
|
||
gint nvalues);
|
||
static void gradient_get_default (const gchar *name,
|
||
guchar *values,
|
||
gint nvalues);
|
||
static void gradient_get_values_external (const gchar *gradient_name,
|
||
guchar *values,
|
||
gint nvalues);
|
||
static void gradient_get_values_real_external (const gchar *gradient_name,
|
||
guchar *values,
|
||
gint nvalues,
|
||
gboolean reverse);
|
||
static GradientCacheItem *gradient_cache_lookup (const gchar *name,
|
||
gboolean *found);
|
||
static void gradient_cache_zorch (void);
|
||
static void gradient_scale_entry_update_double (GimpLabelSpin *entry,
|
||
gdouble *value);
|
||
static void gradient_scale_entry_update_int (GimpLabelSpin *entry,
|
||
gint *value);
|
||
|
||
|
||
G_DEFINE_TYPE (Gflare, gflare, GIMP_TYPE_PLUG_IN)
|
||
|
||
GIMP_MAIN (GFLARE_TYPE)
|
||
DEFINE_STD_SET_I18N
|
||
|
||
|
||
GFlare default_gflare =
|
||
{
|
||
NULL, /* name */
|
||
NULL, /* filename */
|
||
100, /* glow_opacity */
|
||
GF_NORMAL, /* glow_mode */
|
||
100, /* rays_opacity */
|
||
GF_NORMAL, /* rays_mode */
|
||
100, /* sflare_opacity */
|
||
GF_NORMAL, /* sflare_mode */
|
||
"%red_grad", /* glow_radial */
|
||
"%white", /* glow_angular */
|
||
"%white", /* glow_angular_size */
|
||
100.0, /* glow_size */
|
||
0.0, /* glow_rotation */
|
||
0.0, /* glow_hue */
|
||
"%white_grad",/* rays_radial */
|
||
"%random", /* rays_angular */
|
||
"%random", /* rays_angular_size */
|
||
100.0, /* rays_size */
|
||
0.0, /* rays_rotation */
|
||
0.0, /* rays_hue */
|
||
40, /* rays_nspikes */
|
||
20.0, /* rays_thickness */
|
||
"%white_grad",/* sflare_radial */
|
||
"%random", /* sflare_sizefac */
|
||
"%random", /* sflare_probability */
|
||
40.0, /* sflare_size */
|
||
0.0, /* sflare_rotation */
|
||
0.0, /* sflare_hue */
|
||
GF_CIRCLE, /* sflare_shape */
|
||
6, /* sflare_nverts */
|
||
0, /* sflare_seed */
|
||
TRUE, /* random_seed */
|
||
};
|
||
|
||
/* These are keywords to be written to disk files specifying flares. */
|
||
/* They are not translated since we want gflare files to be compatible
|
||
across languages. */
|
||
static const gchar *gflare_modes[] =
|
||
{
|
||
"NORMAL",
|
||
"ADDITION",
|
||
"OVERLAY",
|
||
"SCREEN"
|
||
};
|
||
|
||
static const gchar *gflare_shapes[] =
|
||
{
|
||
"CIRCLE",
|
||
"POLYGON"
|
||
};
|
||
|
||
/* These are for menu entries, so they are translated. */
|
||
static const gchar *gflare_menu_modes[] =
|
||
{
|
||
N_("Normal"),
|
||
N_("Addition"),
|
||
N_("Overlay"),
|
||
N_("Screen")
|
||
};
|
||
|
||
static GimpImage *image;
|
||
static GimpDrawable *drawable;
|
||
static DrawableInfo dinfo;
|
||
static GFlareDialog *dlg = NULL;
|
||
static GFlareEditor *ed = NULL;
|
||
static GList *gflares_list = NULL;
|
||
static gint num_gflares = 0;
|
||
static gchar *gflare_path = NULL;
|
||
static CalcParams calc;
|
||
static GList *gradient_menus;
|
||
static gchar **gradient_names = NULL;
|
||
static gint num_gradient_names = 0;
|
||
static GradientCacheItem *gradient_cache_head = NULL;
|
||
static gint gradient_cache_count = 0;
|
||
|
||
|
||
static const gchar *internal_gradients[] =
|
||
{
|
||
"%white", "%white_grad", "%red_grad", "%blue_grad", "%yellow_grad", "%random"
|
||
};
|
||
|
||
#ifdef DEBUG
|
||
static gint get_values_external_count = 0;
|
||
static clock_t get_values_external_clock = 0;
|
||
#endif
|
||
|
||
|
||
static void
|
||
gflare_class_init (GflareClass *klass)
|
||
{
|
||
GimpPlugInClass *plug_in_class = GIMP_PLUG_IN_CLASS (klass);
|
||
|
||
plug_in_class->query_procedures = gflare_query_procedures;
|
||
plug_in_class->create_procedure = gflare_create_procedure;
|
||
plug_in_class->set_i18n = STD_SET_I18N;
|
||
}
|
||
|
||
static void
|
||
gflare_init (Gflare *gflare)
|
||
{
|
||
}
|
||
|
||
static GList *
|
||
gflare_query_procedures (GimpPlugIn *plug_in)
|
||
{
|
||
return g_list_append (NULL, g_strdup (PLUG_IN_PROC));
|
||
}
|
||
|
||
static GimpProcedure *
|
||
gflare_create_procedure (GimpPlugIn *plug_in,
|
||
const gchar *name)
|
||
{
|
||
GimpProcedure *procedure = NULL;
|
||
|
||
if (! strcmp (name, PLUG_IN_PROC))
|
||
{
|
||
procedure = gimp_image_procedure_new (plug_in, name,
|
||
GIMP_PDB_PROC_TYPE_PLUGIN,
|
||
gflare_run, NULL, NULL);
|
||
|
||
gimp_procedure_set_image_types (procedure, "RGB*, GRAY*");
|
||
gimp_procedure_set_sensitivity_mask (procedure,
|
||
GIMP_PROCEDURE_SENSITIVE_DRAWABLE);
|
||
|
||
gimp_procedure_set_menu_label (procedure, _("_Gradient Flare..."));
|
||
gimp_procedure_add_menu_path (procedure,
|
||
"<Image>/Filters/Light and Shadow/[Light]");
|
||
|
||
gimp_procedure_set_documentation (procedure,
|
||
_("Produce a lense flare effect "
|
||
"using gradients"),
|
||
"This plug-in produces a lense flare "
|
||
"effect using custom gradients. In "
|
||
"interactive call, the user can edit "
|
||
"their own favorite lense flare "
|
||
"(GFlare) and render it. Edited "
|
||
"gflare is saved automatically to "
|
||
"the folder in gflare-path, if it is "
|
||
"defined in gimprc. In "
|
||
"non-interactive call, the user can "
|
||
"only render one of GFlare "
|
||
"which has been stored in "
|
||
"gflare-path already.",
|
||
name);
|
||
gimp_procedure_set_attribution (procedure,
|
||
"Eiichi Takamori",
|
||
"Eiichi Takamori, and a lot of GIMP people",
|
||
"1997");
|
||
|
||
gimp_procedure_add_string_argument (procedure, "gflare-name",
|
||
"GFlare name",
|
||
"Name of the GFlare to render",
|
||
"Default",
|
||
G_PARAM_READWRITE);
|
||
|
||
gimp_procedure_add_int_argument (procedure, "center-x",
|
||
"Center X",
|
||
"X coordinate of center of GFlare",
|
||
-GIMP_MAX_IMAGE_SIZE, GIMP_MAX_IMAGE_SIZE, 128,
|
||
G_PARAM_READWRITE);
|
||
|
||
gimp_procedure_add_int_argument (procedure, "center-y",
|
||
"Center Y",
|
||
"Y coordinate of center of GFlare",
|
||
-GIMP_MAX_IMAGE_SIZE, GIMP_MAX_IMAGE_SIZE, 128,
|
||
G_PARAM_READWRITE);
|
||
|
||
gimp_procedure_add_double_argument (procedure, "radius",
|
||
_("Radi_us"),
|
||
_("Radius of GFlare (pixel)"),
|
||
1, GIMP_MAX_IMAGE_SIZE, 100,
|
||
G_PARAM_READWRITE);
|
||
|
||
gimp_procedure_add_double_argument (procedure, "rotation",
|
||
_("Ro_tation"),
|
||
_("Rotation of GFlare (degree)"),
|
||
0, 360, 0,
|
||
G_PARAM_READWRITE);
|
||
|
||
gimp_procedure_add_double_argument (procedure, "hue",
|
||
_("_Hue rotation"),
|
||
_("Hue rotation of GFlare (degree)"),
|
||
0, 360, 0,
|
||
G_PARAM_READWRITE);
|
||
|
||
gimp_procedure_add_double_argument (procedure, "vector-angle",
|
||
_("Vector _angle"),
|
||
_("Vector angle for second flares (degree)"),
|
||
0, 360, 60,
|
||
G_PARAM_READWRITE);
|
||
|
||
gimp_procedure_add_double_argument (procedure, "vector-length",
|
||
_("Vector len_gth"),
|
||
_("Vector length for second flares "
|
||
"(percentage of Radius)"),
|
||
0, 10000, 400,
|
||
G_PARAM_READWRITE);
|
||
|
||
gimp_procedure_add_boolean_argument (procedure, "use-asupsample",
|
||
_("Ada_ptive supersampling"),
|
||
_("Use adaptive supersampling while rendering"),
|
||
FALSE,
|
||
G_PARAM_READWRITE);
|
||
|
||
gimp_procedure_add_int_argument (procedure, "asupsample-max-depth",
|
||
_("_Max depth"),
|
||
_("Max depth for adaptive supersampling"),
|
||
0, 10, 3,
|
||
G_PARAM_READWRITE);
|
||
|
||
gimp_procedure_add_double_argument (procedure, "asupsample-threshold",
|
||
_("Threshol_d"),
|
||
_("Threshold for adaptive supersampling"),
|
||
0.0, 1.0, 0.2,
|
||
G_PARAM_READWRITE);
|
||
}
|
||
|
||
return procedure;
|
||
}
|
||
|
||
static GimpValueArray *
|
||
gflare_run (GimpProcedure *procedure,
|
||
GimpRunMode run_mode,
|
||
GimpImage *_image,
|
||
gint n_drawables,
|
||
GimpDrawable **drawables,
|
||
GimpProcedureConfig *config,
|
||
gpointer run_data)
|
||
{
|
||
gchar *path;
|
||
|
||
gegl_init (NULL, NULL);
|
||
|
||
image = _image;
|
||
|
||
if (n_drawables != 1)
|
||
{
|
||
GError *error = NULL;
|
||
|
||
g_set_error (&error, GIMP_PLUG_IN_ERROR, 0,
|
||
_("Procedure '%s' only works with one drawable."),
|
||
gimp_procedure_get_name (procedure));
|
||
|
||
return gimp_procedure_new_return_values (procedure,
|
||
GIMP_PDB_CALLING_ERROR,
|
||
error);
|
||
}
|
||
else
|
||
{
|
||
drawable = drawables[0];
|
||
}
|
||
|
||
dinfo.is_color = gimp_drawable_is_rgb (drawable);
|
||
dinfo.has_alpha = gimp_drawable_has_alpha (drawable);
|
||
|
||
if (dinfo.is_color)
|
||
{
|
||
if (dinfo.has_alpha)
|
||
dinfo.format = babl_format ("R'G'B'A u8");
|
||
else
|
||
dinfo.format = babl_format ("R'G'B' u8");
|
||
}
|
||
else
|
||
{
|
||
if (dinfo.has_alpha)
|
||
dinfo.format = babl_format ("Y'A u8");
|
||
else
|
||
dinfo.format = babl_format ("Y u8");
|
||
}
|
||
|
||
dinfo.bpp = babl_format_get_bytes_per_pixel (dinfo.format);
|
||
|
||
if (! gimp_drawable_mask_intersect (drawable,
|
||
&dinfo.x, &dinfo.y, &dinfo.w, &dinfo.h))
|
||
{
|
||
return gimp_procedure_new_return_values (procedure,
|
||
GIMP_PDB_SUCCESS,
|
||
NULL);
|
||
}
|
||
|
||
/*
|
||
* Start gradient caching
|
||
*/
|
||
|
||
gradient_init ();
|
||
|
||
/*
|
||
* Parse gflare path from gimprc and load gflares
|
||
*/
|
||
|
||
path = gimp_gimprc_query ("gflare-path");
|
||
if (path)
|
||
{
|
||
gflare_path = g_filename_from_utf8 (path, -1, NULL, NULL, NULL);
|
||
g_free (path);
|
||
}
|
||
else
|
||
{
|
||
GFile *gimprc = gimp_directory_file ("gimprc", NULL);
|
||
gchar *full_path = gimp_config_build_data_path ("gflare");
|
||
gchar *esc_path = g_strescape (full_path, NULL);
|
||
g_free (full_path);
|
||
|
||
g_message (_("No %s in gimprc:\n"
|
||
"You need to add an entry like\n"
|
||
"(%s \"%s\")\n"
|
||
"to your %s file."),
|
||
"gflare-path", "gflare-path",
|
||
esc_path, gimp_file_get_utf8_name (gimprc));
|
||
|
||
g_object_unref (gimprc);
|
||
g_free (esc_path);
|
||
}
|
||
|
||
gflares_list_load_all ();
|
||
|
||
if (run_mode == GIMP_RUN_INTERACTIVE && ! dlg_run (procedure, config))
|
||
return gimp_procedure_new_return_values (procedure,
|
||
GIMP_PDB_CANCEL,
|
||
NULL);
|
||
|
||
if (gimp_drawable_is_rgb (drawable) ||
|
||
gimp_drawable_is_gray (drawable))
|
||
{
|
||
gimp_progress_init (_("Gradient Flare"));
|
||
|
||
plugin_do (config);
|
||
|
||
if (run_mode != GIMP_RUN_NONINTERACTIVE)
|
||
gimp_displays_flush ();
|
||
}
|
||
else
|
||
{
|
||
GError *error =
|
||
g_error_new_literal (0, 0,
|
||
_("Cannot operate on indexed color images."));
|
||
|
||
return gimp_procedure_new_return_values (procedure,
|
||
GIMP_PDB_EXECUTION_ERROR,
|
||
error);
|
||
}
|
||
|
||
/*
|
||
* Deinitialization
|
||
*/
|
||
gradient_free ();
|
||
|
||
return gimp_procedure_new_return_values (procedure, GIMP_PDB_SUCCESS, NULL);
|
||
}
|
||
|
||
static void
|
||
plugin_do (GimpProcedureConfig *config)
|
||
{
|
||
GeglBuffer *src_buffer;
|
||
GeglBuffer *dest_buffer;
|
||
GFlare *gflare;
|
||
gchar *gflare_name;
|
||
gint xcenter;
|
||
gint ycenter;
|
||
gdouble radius;
|
||
gdouble rotation;
|
||
gdouble hue;
|
||
gdouble vangle;
|
||
gdouble vlength;
|
||
gboolean use_asupsample;
|
||
gint asupsample_max_depth;
|
||
gdouble asupsample_threshold;
|
||
|
||
g_object_get (config,
|
||
"gflare-name", &gflare_name,
|
||
"center-x", &xcenter,
|
||
"center-y", &ycenter,
|
||
"radius", &radius,
|
||
"rotation", &rotation,
|
||
"hue", &hue,
|
||
"vector-angle", &vangle,
|
||
"vector-length", &vlength,
|
||
"use-asupsample", &use_asupsample,
|
||
"asupsample-max-depth", &asupsample_max_depth,
|
||
"asupsample-threshold", &asupsample_threshold,
|
||
NULL);
|
||
|
||
gflare = gflares_list_lookup (gflare_name);
|
||
if (gflare == NULL)
|
||
{
|
||
/* FIXME */
|
||
g_warning ("Not found %s\n", gflare_name);
|
||
g_free (gflare_name);
|
||
return;
|
||
}
|
||
g_free (gflare_name);
|
||
|
||
/* Initialize calc params and gradients */
|
||
calc_init_params (gflare, CALC_GLOW | CALC_RAYS | CALC_SFLARE,
|
||
xcenter, ycenter,
|
||
radius, rotation, hue,
|
||
vangle, vlength);
|
||
while (calc_init_progress ()) ;
|
||
|
||
src_buffer = gimp_drawable_get_buffer (drawable);
|
||
dest_buffer = gimp_drawable_get_shadow_buffer (drawable);
|
||
|
||
/* Render it ! */
|
||
if (use_asupsample)
|
||
plugin_do_asupsample (src_buffer, dest_buffer, asupsample_max_depth, asupsample_threshold);
|
||
else
|
||
plugin_do_non_asupsample (src_buffer, dest_buffer);
|
||
|
||
g_object_unref (src_buffer);
|
||
g_object_unref (dest_buffer);
|
||
|
||
gimp_progress_update (1.0);
|
||
|
||
/* Clean up */
|
||
calc_deinit ();
|
||
|
||
gimp_drawable_merge_shadow (drawable, TRUE);
|
||
gimp_drawable_update (drawable, dinfo.x, dinfo.y, dinfo.w, dinfo.h);
|
||
}
|
||
|
||
/* these routines should be almost rewritten anyway */
|
||
|
||
static void
|
||
plugin_do_non_asupsample (GeglBuffer *src_buffer,
|
||
GeglBuffer *dest_buffer)
|
||
{
|
||
GeglBufferIterator *iter;
|
||
gint progress;
|
||
gint max_progress;
|
||
|
||
progress = 0;
|
||
max_progress = dinfo.w * dinfo.h;
|
||
|
||
iter = gegl_buffer_iterator_new (src_buffer,
|
||
GEGL_RECTANGLE (dinfo.x, dinfo.y,
|
||
dinfo.w, dinfo.h), 0,
|
||
dinfo.format,
|
||
GEGL_ACCESS_READ, GEGL_ABYSS_NONE, 2);
|
||
|
||
gegl_buffer_iterator_add (iter, dest_buffer,
|
||
GEGL_RECTANGLE (dinfo.x, dinfo.y,
|
||
dinfo.w, dinfo.h), 0,
|
||
dinfo.format,
|
||
GEGL_ACCESS_WRITE, GEGL_ABYSS_NONE);
|
||
|
||
while (gegl_buffer_iterator_next (iter))
|
||
{
|
||
const guchar *src_row = iter->items[0].data;
|
||
guchar *dest_row = iter->items[1].data;
|
||
gint row, y;
|
||
|
||
for (row = 0, y = iter->items[0].roi.y;
|
||
row < iter->items[0].roi.height;
|
||
row++, y++)
|
||
{
|
||
const guchar *src = src_row;
|
||
guchar *dest = dest_row;
|
||
gint col, x;
|
||
|
||
for (col = 0, x = iter->items[0].roi.x;
|
||
col < iter->items[0].roi.width;
|
||
col++, x++)
|
||
{
|
||
guchar src_pix[4];
|
||
guchar dest_pix[4];
|
||
gint b;
|
||
|
||
for (b = 0; b < 3; b++)
|
||
src_pix[b] = dinfo.is_color ? src[b] : src[0];
|
||
|
||
src_pix[3] = dinfo.has_alpha ? src[dinfo.bpp - 1] : OPAQUE;
|
||
|
||
calc_gflare_pix (dest_pix, x, y, src_pix);
|
||
|
||
if (dinfo.is_color)
|
||
{
|
||
for (b = 0; b < 3; b++)
|
||
dest[b] = dest_pix[b];
|
||
}
|
||
else
|
||
{
|
||
dest[0] = LUMINOSITY (dest_pix);
|
||
}
|
||
|
||
if (dinfo.has_alpha)
|
||
dest[dinfo.bpp - 1] = dest_pix[3];
|
||
|
||
src += dinfo.bpp;
|
||
dest += dinfo.bpp;
|
||
}
|
||
|
||
src_row += dinfo.bpp * iter->items[0].roi.width;
|
||
dest_row += dinfo.bpp * iter->items[1].roi.width;
|
||
}
|
||
|
||
/* Update progress */
|
||
progress += iter->items[0].roi.width * iter->items[0].roi.height;
|
||
gimp_progress_update ((double) progress / (double) max_progress);
|
||
}
|
||
}
|
||
|
||
static void
|
||
plugin_do_asupsample (GeglBuffer *src_buffer,
|
||
GeglBuffer *dest_buffer,
|
||
gint asupsample_max_depth,
|
||
gdouble asupsample_threshold)
|
||
{
|
||
gimp_adaptive_supersample_area (dinfo.x, dinfo.y,
|
||
dinfo.x + dinfo.w - 1, dinfo.y + dinfo.h - 1,
|
||
asupsample_max_depth,
|
||
asupsample_threshold,
|
||
plugin_render_func,
|
||
src_buffer,
|
||
plugin_put_pixel_func,
|
||
dest_buffer,
|
||
plugin_progress_func,
|
||
NULL);
|
||
}
|
||
|
||
/*
|
||
Adaptive supersampling callback functions
|
||
|
||
These routines may look messy, since adaptive supersampling needs
|
||
pixel values in `double' (from 0.0 to 1.0) but calc_*_pix () returns
|
||
guchar values. */
|
||
|
||
static void
|
||
plugin_render_func (gdouble x,
|
||
gdouble y,
|
||
GimpRGB *color,
|
||
gpointer data)
|
||
{
|
||
GeglBuffer *src_buffer = data;
|
||
guchar src_pix[4];
|
||
guchar flare_pix[4];
|
||
guchar src[4];
|
||
gint b;
|
||
gint ix, iy;
|
||
|
||
/* translate (0.5, 0.5) before convert to `int' so that it can surely
|
||
point the center of pixel */
|
||
ix = floor (x + 0.5);
|
||
iy = floor (y + 0.5);
|
||
|
||
gegl_buffer_sample (src_buffer, ix, iy, NULL, src, dinfo.format,
|
||
GEGL_SAMPLER_NEAREST, GEGL_ABYSS_NONE);
|
||
|
||
for (b = 0; b < 3; b++)
|
||
src_pix[b] = dinfo.is_color ? src[b] : src[0];
|
||
src_pix[3] = dinfo.has_alpha ? src[dinfo.bpp - 1] : OPAQUE;
|
||
|
||
calc_gflare_pix (flare_pix, x, y, src_pix);
|
||
|
||
color->r = flare_pix[0] / 255.0;
|
||
color->g = flare_pix[1] / 255.0;
|
||
color->b = flare_pix[2] / 255.0;
|
||
color->a = flare_pix[3] / 255.0;
|
||
}
|
||
|
||
static void
|
||
plugin_put_pixel_func (gint ix,
|
||
gint iy,
|
||
GimpRGB *color,
|
||
gpointer data)
|
||
{
|
||
GeglBuffer *dest_buffer = data;
|
||
guchar dest[4];
|
||
|
||
if (dinfo.is_color)
|
||
{
|
||
dest[0] = color->r * 255;
|
||
dest[1] = color->g * 255;
|
||
dest[2] = color->b * 255;
|
||
}
|
||
else
|
||
{
|
||
dest[0] = gimp_rgb_luminance_uchar (color);
|
||
}
|
||
|
||
if (dinfo.has_alpha)
|
||
dest[dinfo.bpp - 1] = color->a * 255;
|
||
|
||
gegl_buffer_set (dest_buffer, GEGL_RECTANGLE (ix, iy, 1, 1), 0,
|
||
dinfo.format, dest, GEGL_AUTO_ROWSTRIDE);
|
||
}
|
||
|
||
static void
|
||
plugin_progress_func (gint y1,
|
||
gint y2,
|
||
gint curr_y,
|
||
gpointer data)
|
||
{
|
||
gimp_progress_update ((double) curr_y / (double) (y2 - y1));
|
||
}
|
||
|
||
/*************************************************************************/
|
||
/** **/
|
||
/** +++ GFlare Routines **/
|
||
/** **/
|
||
/*************************************************************************/
|
||
|
||
/*
|
||
* These code are more or less based on Quartic's gradient.c,
|
||
* other gimp sources, and script-fu.
|
||
*/
|
||
|
||
static GFlare *
|
||
gflare_new (void)
|
||
{
|
||
GFlare *gflare = g_new0 (GFlare, 1);
|
||
|
||
gflare->name = NULL;
|
||
gflare->filename = NULL;
|
||
|
||
return gflare;
|
||
}
|
||
|
||
static GFlare *
|
||
gflare_new_with_default (const gchar *new_name)
|
||
{
|
||
return gflare_dup (&default_gflare, new_name);
|
||
}
|
||
|
||
static GFlare *
|
||
gflare_dup (const GFlare *src,
|
||
const gchar *new_name)
|
||
{
|
||
GFlare *dest = g_new0 (GFlare, 1);
|
||
|
||
*dest = *src;
|
||
|
||
dest->name = g_strdup (new_name);
|
||
dest->filename = NULL;
|
||
|
||
return dest;
|
||
}
|
||
|
||
static void
|
||
gflare_copy (GFlare *dest,
|
||
const GFlare *src)
|
||
{
|
||
gchar *name, *filename;
|
||
|
||
name = dest->name;
|
||
filename = dest->filename;
|
||
|
||
*dest = *src;
|
||
|
||
dest->name = name;
|
||
dest->filename =filename;
|
||
}
|
||
|
||
|
||
static void
|
||
gflare_free (GFlare *gflare)
|
||
{
|
||
g_return_if_fail (gflare != NULL);
|
||
|
||
g_free (gflare->name);
|
||
g_free (gflare->filename);
|
||
g_free (gflare);
|
||
}
|
||
|
||
GFlare *
|
||
gflare_load (const gchar *filename,
|
||
const gchar *name)
|
||
{
|
||
FILE *fp;
|
||
GFlareFile *gf;
|
||
GFlare *gflare;
|
||
gchar header[256];
|
||
|
||
g_return_val_if_fail (filename != NULL, NULL);
|
||
|
||
fp = g_fopen (filename, "rb");
|
||
if (!fp)
|
||
{
|
||
g_message (_("Failed to open GFlare file '%s': %s"),
|
||
gimp_filename_to_utf8 (filename), g_strerror (errno));
|
||
return NULL;
|
||
}
|
||
|
||
if (fgets (header, sizeof(header), fp) == NULL
|
||
|| strcmp (header, GFLARE_FILE_HEADER) != 0)
|
||
{
|
||
g_warning (_("'%s' is not a valid GFlare file."),
|
||
gimp_filename_to_utf8 (filename));
|
||
fclose (fp);
|
||
return NULL;
|
||
}
|
||
|
||
gf = g_new (GFlareFile, 1);
|
||
gf->fp = fp;
|
||
gf->error = FALSE;
|
||
|
||
gflare = gflare_new ();
|
||
gflare->name = g_strdup (name);
|
||
gflare->filename = g_strdup (filename);
|
||
|
||
gflare_read_double (&gflare->glow_opacity, gf);
|
||
gflare_read_mode (&gflare->glow_mode, gf);
|
||
gflare_read_double (&gflare->rays_opacity, gf);
|
||
gflare_read_mode (&gflare->rays_mode, gf);
|
||
gflare_read_double (&gflare->sflare_opacity, gf);
|
||
gflare_read_mode (&gflare->sflare_mode, gf);
|
||
|
||
gflare_read_gradient_name (gflare->glow_radial, gf);
|
||
gflare_read_gradient_name (gflare->glow_angular, gf);
|
||
gflare_read_gradient_name (gflare->glow_angular_size, gf);
|
||
gflare_read_double (&gflare->glow_size, gf);
|
||
gflare_read_double (&gflare->glow_rotation, gf);
|
||
gflare_read_double (&gflare->glow_hue, gf);
|
||
|
||
gflare_read_gradient_name (gflare->rays_radial, gf);
|
||
gflare_read_gradient_name (gflare->rays_angular, gf);
|
||
gflare_read_gradient_name (gflare->rays_angular_size, gf);
|
||
gflare_read_double (&gflare->rays_size, gf);
|
||
gflare_read_double (&gflare->rays_rotation, gf);
|
||
gflare_read_double (&gflare->rays_hue, gf);
|
||
gflare_read_int (&gflare->rays_nspikes, gf);
|
||
gflare_read_double (&gflare->rays_thickness, gf);
|
||
|
||
gflare_read_gradient_name (gflare->sflare_radial, gf);
|
||
gflare_read_gradient_name (gflare->sflare_sizefac, gf);
|
||
gflare_read_gradient_name (gflare->sflare_probability, gf);
|
||
gflare_read_double (&gflare->sflare_size, gf);
|
||
gflare_read_double (&gflare->sflare_hue, gf);
|
||
gflare_read_double (&gflare->sflare_rotation, gf);
|
||
gflare_read_shape (&gflare->sflare_shape, gf);
|
||
gflare_read_int (&gflare->sflare_nverts, gf);
|
||
gflare_read_int ((gint *) &gflare->sflare_seed, gf);
|
||
|
||
if (gflare->sflare_seed == 0)
|
||
gflare->sflare_seed = g_random_int();
|
||
|
||
fclose (gf->fp);
|
||
|
||
if (gf->error)
|
||
{
|
||
g_warning (_("invalid formatted GFlare file: %s\n"), filename);
|
||
g_free (gflare);
|
||
g_free (gf);
|
||
return NULL;
|
||
}
|
||
|
||
g_free (gf);
|
||
|
||
return gflare;
|
||
}
|
||
|
||
static void
|
||
gflare_read_int (gint *intvar,
|
||
GFlareFile *gf)
|
||
{
|
||
if (gf->error)
|
||
return;
|
||
|
||
if (fscanf (gf->fp, "%d", intvar) != 1)
|
||
gf->error = TRUE;
|
||
}
|
||
|
||
static void
|
||
gflare_read_double (gdouble *dblvar,
|
||
GFlareFile *gf)
|
||
{
|
||
gchar buf[31];
|
||
|
||
if (gf->error)
|
||
return;
|
||
|
||
if (fscanf (gf->fp, "%30s", buf) == 1)
|
||
*dblvar = g_ascii_strtod (buf, NULL);
|
||
else
|
||
gf->error = TRUE;
|
||
}
|
||
|
||
static void
|
||
gflare_read_gradient_name (GradientName name,
|
||
GFlareFile *gf)
|
||
{
|
||
gchar tmp[1024], dec[1024];
|
||
|
||
if (gf->error)
|
||
return;
|
||
|
||
/* FIXME: this is buggy */
|
||
|
||
if (fscanf (gf->fp, "%1023s", tmp) == 1)
|
||
{
|
||
/* @GRADIENT_NAME */
|
||
gradient_name_decode (dec, tmp);
|
||
gradient_name_copy (name, dec);
|
||
}
|
||
else
|
||
gf->error = TRUE;
|
||
}
|
||
|
||
static void
|
||
gflare_read_shape (GFlareShape *shape,
|
||
GFlareFile *gf)
|
||
{
|
||
gchar tmp[1024];
|
||
gint i;
|
||
|
||
if (gf->error)
|
||
return;
|
||
|
||
if (fscanf (gf->fp, "%1023s", tmp) == 1)
|
||
{
|
||
for (i = 0; i < GF_NUM_SHAPES; i++)
|
||
if (strcmp (tmp, gflare_shapes[i]) == 0)
|
||
{
|
||
*shape = i;
|
||
return;
|
||
}
|
||
}
|
||
gf->error = TRUE;
|
||
}
|
||
|
||
static void
|
||
gflare_read_mode (GFlareMode *mode,
|
||
GFlareFile *gf)
|
||
{
|
||
gchar tmp[1024];
|
||
gint i;
|
||
|
||
if (gf->error)
|
||
return;
|
||
|
||
if (fscanf (gf->fp, "%1023s", tmp) == 1)
|
||
{
|
||
for (i = 0; i < GF_NUM_MODES; i++)
|
||
if (strcmp (tmp, gflare_modes[i]) == 0)
|
||
{
|
||
*mode = i;
|
||
return;
|
||
}
|
||
}
|
||
gf->error = TRUE;
|
||
}
|
||
|
||
static void
|
||
gflare_save (GFlare *gflare)
|
||
{
|
||
FILE *fp;
|
||
gchar *path;
|
||
gchar buf[3][G_ASCII_DTOSTR_BUF_SIZE];
|
||
static gboolean message_ok = FALSE;
|
||
|
||
if (gflare->filename == NULL)
|
||
{
|
||
GList *list;
|
||
|
||
if (gflare_path == NULL)
|
||
{
|
||
if (! message_ok)
|
||
{
|
||
GFile *gimprc = gimp_directory_file ("gimprc", NULL);
|
||
GFile *dir = gimp_directory_file ("gflare", NULL);
|
||
gchar *gflare_dir;
|
||
|
||
gflare_dir =
|
||
g_strescape ("${gimp_dir}" G_DIR_SEPARATOR_S "gflare", NULL);
|
||
|
||
g_message (_("GFlare '%s' is not saved. If you add a new entry "
|
||
"in '%s', like:\n"
|
||
"(gflare-path \"%s\")\n"
|
||
"and make a folder '%s', then you can save "
|
||
"your own GFlares into that folder."),
|
||
gflare->name,
|
||
gimp_file_get_utf8_name (gimprc),
|
||
gflare_dir,
|
||
gimp_file_get_utf8_name (dir));
|
||
|
||
g_object_unref (gimprc);
|
||
g_object_unref (dir);
|
||
g_free (gflare_dir);
|
||
|
||
message_ok = TRUE;
|
||
}
|
||
|
||
return;
|
||
}
|
||
|
||
list = gimp_path_parse (gflare_path, 256, FALSE, NULL);
|
||
path = gimp_path_get_user_writable_dir (list);
|
||
gimp_path_free (list);
|
||
|
||
if (! path)
|
||
path = g_strdup (gimp_directory ());
|
||
|
||
gflare->filename = g_build_filename (path, gflare->name, NULL);
|
||
|
||
g_free (path);
|
||
}
|
||
|
||
fp = g_fopen (gflare->filename, "wb");
|
||
if (!fp)
|
||
{
|
||
g_message (_("Failed to write GFlare file '%s': %s"),
|
||
gimp_filename_to_utf8 (gflare->filename), g_strerror (errno));
|
||
return;
|
||
}
|
||
|
||
fprintf (fp, "%s", GFLARE_FILE_HEADER);
|
||
g_ascii_dtostr (buf[0],
|
||
G_ASCII_DTOSTR_BUF_SIZE, gflare->glow_opacity);
|
||
fprintf (fp, "%s %s\n", buf[0], gflare_modes[gflare->glow_mode]);
|
||
g_ascii_dtostr (buf[0],
|
||
G_ASCII_DTOSTR_BUF_SIZE, gflare->rays_opacity);
|
||
fprintf (fp, "%s %s\n", buf[0], gflare_modes[gflare->rays_mode]);
|
||
g_ascii_dtostr (buf[0],
|
||
G_ASCII_DTOSTR_BUF_SIZE, gflare->sflare_opacity);
|
||
fprintf (fp, "%s %s\n", buf[0], gflare_modes[gflare->sflare_mode]);
|
||
|
||
gflare_write_gradient_name (gflare->glow_radial, fp);
|
||
gflare_write_gradient_name (gflare->glow_angular, fp);
|
||
gflare_write_gradient_name (gflare->glow_angular_size, fp);
|
||
g_ascii_dtostr (buf[0],
|
||
G_ASCII_DTOSTR_BUF_SIZE, gflare->glow_size);
|
||
g_ascii_dtostr (buf[1],
|
||
G_ASCII_DTOSTR_BUF_SIZE, gflare->glow_rotation);
|
||
g_ascii_dtostr (buf[2],
|
||
G_ASCII_DTOSTR_BUF_SIZE, gflare->glow_hue);
|
||
fprintf (fp, "%s %s %s\n", buf[0], buf[1], buf[2]);
|
||
|
||
gflare_write_gradient_name (gflare->rays_radial, fp);
|
||
gflare_write_gradient_name (gflare->rays_angular, fp);
|
||
gflare_write_gradient_name (gflare->rays_angular_size, fp);
|
||
g_ascii_dtostr (buf[0],
|
||
G_ASCII_DTOSTR_BUF_SIZE, gflare->rays_size);
|
||
g_ascii_dtostr (buf[1],
|
||
G_ASCII_DTOSTR_BUF_SIZE, gflare->rays_rotation);
|
||
g_ascii_dtostr (buf[2],
|
||
G_ASCII_DTOSTR_BUF_SIZE, gflare->rays_hue);
|
||
fprintf (fp, "%s %s %s\n", buf[0], buf[1], buf[2]);
|
||
g_ascii_dtostr (buf[0],
|
||
G_ASCII_DTOSTR_BUF_SIZE, gflare->rays_thickness);
|
||
fprintf (fp, "%d %s\n", gflare->rays_nspikes, buf[0]);
|
||
|
||
gflare_write_gradient_name (gflare->sflare_radial, fp);
|
||
gflare_write_gradient_name (gflare->sflare_sizefac, fp);
|
||
gflare_write_gradient_name (gflare->sflare_probability, fp);
|
||
g_ascii_dtostr (buf[0],
|
||
G_ASCII_DTOSTR_BUF_SIZE, gflare->sflare_size);
|
||
g_ascii_dtostr (buf[1],
|
||
G_ASCII_DTOSTR_BUF_SIZE, gflare->sflare_rotation);
|
||
g_ascii_dtostr (buf[2],
|
||
G_ASCII_DTOSTR_BUF_SIZE, gflare->sflare_hue);
|
||
fprintf (fp, "%s %s %s\n", buf[0], buf[1], buf[2]);
|
||
fprintf (fp, "%s %d %d\n",
|
||
gflare_shapes[gflare->sflare_shape],
|
||
gflare->sflare_nverts, gflare->sflare_seed);
|
||
|
||
fclose (fp);
|
||
}
|
||
|
||
static void
|
||
gflare_write_gradient_name (GradientName name,
|
||
FILE *fp)
|
||
{
|
||
gchar enc[1024];
|
||
|
||
/* @GRADIENT_NAME */
|
||
|
||
/* encode white spaces and control characters (if any) */
|
||
gradient_name_encode (enc, name);
|
||
|
||
fprintf (fp, "%s\n", enc);
|
||
}
|
||
|
||
/*************************************************************************/
|
||
/** **/
|
||
/** +++ GFlares List **/
|
||
/** **/
|
||
/*************************************************************************/
|
||
|
||
static gint
|
||
gflare_compare (const GFlare *flare1,
|
||
const GFlare *flare2)
|
||
{
|
||
return strcmp (flare1->name, flare2->name);
|
||
}
|
||
|
||
static gint
|
||
gflares_list_insert (GFlare *gflare)
|
||
{
|
||
num_gflares++;
|
||
gflares_list = g_list_insert_sorted (gflares_list, gflare,
|
||
(GCompareFunc) gflare_compare);
|
||
return gflares_list_index (gflare);
|
||
}
|
||
|
||
static gint
|
||
gflare_compare_name (const GFlare *flare,
|
||
const gchar *name)
|
||
{
|
||
return strcmp (flare->name, name);
|
||
}
|
||
|
||
static GFlare *
|
||
gflares_list_lookup (const gchar *name)
|
||
{
|
||
GList *llink;
|
||
llink = g_list_find_custom (gflares_list, name,
|
||
(GCompareFunc) gflare_compare_name);
|
||
return (llink) ? llink->data : NULL;
|
||
}
|
||
|
||
static gint
|
||
gflares_list_index (GFlare *gflare)
|
||
{
|
||
return g_list_index (gflares_list, gflare);
|
||
}
|
||
|
||
static gint
|
||
gflares_list_remove (GFlare *gflare)
|
||
{
|
||
GList *tmp;
|
||
gint n;
|
||
|
||
n = 0;
|
||
tmp = gflares_list;
|
||
while (tmp)
|
||
{
|
||
if (tmp->data == gflare)
|
||
{
|
||
/* Found! */
|
||
if (tmp->next == NULL)
|
||
num_gflares--;
|
||
gflares_list = g_list_remove (gflares_list, gflare);
|
||
return n;
|
||
}
|
||
tmp = tmp->next;
|
||
n++;
|
||
}
|
||
return -1;
|
||
}
|
||
|
||
/*
|
||
* Load all gflares, which are founded in gflare-path-list, into gflares_list.
|
||
*/
|
||
static void
|
||
gflares_list_load_all (void)
|
||
{
|
||
GList *path;
|
||
GList *list;
|
||
|
||
/* Make sure to clear any existing gflares */
|
||
gflares_list_free_all ();
|
||
|
||
path = gimp_config_path_expand_to_files (gflare_path, NULL);
|
||
|
||
for (list = path; list; list = g_list_next (list))
|
||
{
|
||
GFileEnumerator *enumerator;
|
||
|
||
enumerator = g_file_enumerate_children (list->data,
|
||
G_FILE_ATTRIBUTE_STANDARD_NAME ","
|
||
G_FILE_ATTRIBUTE_STANDARD_IS_HIDDEN ","
|
||
G_FILE_ATTRIBUTE_STANDARD_TYPE,
|
||
G_FILE_QUERY_INFO_NONE,
|
||
NULL, NULL);
|
||
|
||
if (enumerator)
|
||
{
|
||
GFileInfo *info;
|
||
|
||
while ((info = g_file_enumerator_next_file (enumerator, NULL, NULL)))
|
||
{
|
||
GFileType file_type = g_file_info_get_attribute_uint32 (info, G_FILE_ATTRIBUTE_STANDARD_TYPE);
|
||
|
||
if (file_type == G_FILE_TYPE_REGULAR &&
|
||
! g_file_info_get_attribute_boolean (info, G_FILE_ATTRIBUTE_STANDARD_IS_HIDDEN))
|
||
{
|
||
GFlare *gflare;
|
||
GFile *child;
|
||
gchar *filename;
|
||
gchar *basename;
|
||
|
||
child = g_file_enumerator_get_child (enumerator, info);
|
||
|
||
filename = g_file_get_path (child);
|
||
basename = g_file_get_basename (child);
|
||
|
||
gflare = gflare_load (filename, basename);
|
||
|
||
g_free (filename);
|
||
g_free (basename);
|
||
|
||
if (gflare)
|
||
gflares_list_insert (gflare);
|
||
|
||
g_object_unref (child);
|
||
}
|
||
|
||
g_object_unref (info);
|
||
}
|
||
|
||
g_object_unref (enumerator);
|
||
}
|
||
}
|
||
|
||
g_list_free_full (path, (GDestroyNotify) g_object_unref);
|
||
}
|
||
|
||
static void
|
||
gflares_list_free_all (void)
|
||
{
|
||
g_list_free_full (gflares_list, (GDestroyNotify) gflare_free);
|
||
gflares_list = NULL;
|
||
}
|
||
|
||
/*************************************************************************/
|
||
/** **/
|
||
/** +++ Calculator **/
|
||
/** **/
|
||
/*************************************************************************/
|
||
|
||
|
||
/*
|
||
* These routines calculates pixel values of particular gflare, at
|
||
* specified point. The client which wants to get benefit from these
|
||
* calculation routines must call calc_init_params() first, and
|
||
* iterate calling calc_init_progress() until it returns FALSE. and
|
||
* must call calc_deinit() when job is done.
|
||
*/
|
||
|
||
static void
|
||
calc_init_params (GFlare *gflare,
|
||
gint calc_type,
|
||
gint xcenter,
|
||
gint ycenter,
|
||
gdouble radius,
|
||
gdouble rotation,
|
||
gdouble hue,
|
||
gdouble vangle,
|
||
gdouble vlength)
|
||
{
|
||
calc.type = calc_type;
|
||
calc.gflare = gflare;
|
||
calc.xcenter = xcenter;
|
||
calc.ycenter = ycenter;
|
||
calc.radius = radius;
|
||
calc.rotation = rotation * G_PI / 180.0;
|
||
calc.hue = hue;
|
||
calc.vangle = vangle * G_PI / 180.0;
|
||
calc.vlength = radius * vlength / 100.0;
|
||
calc.glow_radius = radius * gflare->glow_size / 100.0;
|
||
calc.rays_radius = radius * gflare->rays_size / 100.0;
|
||
calc.sflare_radius = radius * gflare->sflare_size / 100.0;
|
||
calc.glow_rotation = (rotation + gflare->glow_rotation) * G_PI / 180.0;
|
||
calc.rays_rotation = (rotation + gflare->rays_rotation) * G_PI / 180.0;
|
||
calc.sflare_rotation = (rotation + gflare->sflare_rotation) * G_PI / 180.0;
|
||
calc.glow_opacity = gflare->glow_opacity * 255 / 100.0;
|
||
calc.rays_opacity = gflare->rays_opacity * 255 / 100.0;
|
||
calc.sflare_opacity = gflare->sflare_opacity * 255 / 100.0;
|
||
|
||
calc.glow_bounds.x0 = calc.xcenter - calc.glow_radius - 0.1;
|
||
calc.glow_bounds.x1 = calc.xcenter + calc.glow_radius + 0.1;
|
||
calc.glow_bounds.y0 = calc.ycenter - calc.glow_radius - 0.1;
|
||
calc.glow_bounds.y1 = calc.ycenter + calc.glow_radius + 0.1;
|
||
calc.rays_bounds.x0 = calc.xcenter - calc.rays_radius - 0.1;
|
||
calc.rays_bounds.x1 = calc.xcenter + calc.rays_radius + 0.1;
|
||
calc.rays_bounds.y0 = calc.ycenter - calc.rays_radius - 0.1;
|
||
calc.rays_bounds.y1 = calc.ycenter + calc.rays_radius + 0.1;
|
||
|
||
/* Thanks to Marcelo Malheiros for this algorithm */
|
||
calc.rays_thinness = log (gflare->rays_thickness / 100.0) / log(0.8);
|
||
|
||
calc.rays_spike_mod = 1.0 / (2 * gflare->rays_nspikes);
|
||
|
||
/*
|
||
Initialize part of sflare
|
||
The rest will be initialized in calc_sflare()
|
||
*/
|
||
calc.sflare_list = NULL;
|
||
calc.sflare_shape = gflare->sflare_shape;
|
||
if (calc.sflare_shape == GF_POLYGON)
|
||
{
|
||
calc.sflare_angle = 2 * G_PI / (2 * gflare->sflare_nverts);
|
||
calc.sflare_factor = 1.0 / cos (calc.sflare_angle);
|
||
}
|
||
|
||
calc.glow_radial = NULL;
|
||
calc.glow_angular = NULL;
|
||
calc.glow_angular_size = NULL;
|
||
calc.rays_radial = NULL;
|
||
calc.rays_angular = NULL;
|
||
calc.rays_angular_size = NULL;
|
||
calc.sflare_radial = NULL;
|
||
calc.sflare_sizefac = NULL;
|
||
calc.sflare_probability = NULL;
|
||
|
||
calc.init = TRUE;
|
||
}
|
||
|
||
static int
|
||
calc_init_progress (void)
|
||
{
|
||
if (calc_sample_one_gradient ())
|
||
return TRUE;
|
||
calc_place_sflare ();
|
||
return FALSE;
|
||
}
|
||
|
||
/*
|
||
Store samples of gradient into an array
|
||
this routine is called during Calc initialization
|
||
this code is very messy... :( */
|
||
static int
|
||
calc_sample_one_gradient (void)
|
||
{
|
||
static struct
|
||
{
|
||
guchar **values;
|
||
gint name_offset;
|
||
gint hue_offset;
|
||
gint gray;
|
||
} table[] = {
|
||
{
|
||
&calc.glow_radial,
|
||
G_STRUCT_OFFSET (GFlare, glow_radial),
|
||
G_STRUCT_OFFSET (GFlare, glow_hue),
|
||
FALSE
|
||
},
|
||
{
|
||
&calc.glow_angular,
|
||
G_STRUCT_OFFSET (GFlare, glow_angular),
|
||
0,
|
||
FALSE
|
||
},
|
||
{
|
||
&calc.glow_angular_size,
|
||
G_STRUCT_OFFSET (GFlare, glow_angular_size),
|
||
0,
|
||
TRUE
|
||
},
|
||
{
|
||
&calc.rays_radial,
|
||
G_STRUCT_OFFSET (GFlare, rays_radial),
|
||
G_STRUCT_OFFSET (GFlare, rays_hue),
|
||
FALSE
|
||
},
|
||
{
|
||
&calc.rays_angular,
|
||
G_STRUCT_OFFSET (GFlare, rays_angular),
|
||
0,
|
||
FALSE
|
||
},
|
||
{
|
||
&calc.rays_angular_size,
|
||
G_STRUCT_OFFSET (GFlare, rays_angular_size),
|
||
0,
|
||
TRUE
|
||
},
|
||
{
|
||
&calc.sflare_radial,
|
||
G_STRUCT_OFFSET (GFlare, sflare_radial),
|
||
G_STRUCT_OFFSET (GFlare, sflare_hue),
|
||
FALSE
|
||
},
|
||
{
|
||
&calc.sflare_sizefac,
|
||
G_STRUCT_OFFSET (GFlare, sflare_sizefac),
|
||
0,
|
||
TRUE
|
||
},
|
||
{
|
||
&calc.sflare_probability,
|
||
G_STRUCT_OFFSET (GFlare, sflare_probability),
|
||
0,
|
||
TRUE
|
||
}
|
||
};
|
||
GFlare *gflare = calc.gflare;
|
||
GradientName *grad_name;
|
||
guchar *gradient;
|
||
gdouble hue_deg;
|
||
gint i, j, hue;
|
||
|
||
for (i = 0; i < G_N_ELEMENTS (table); i++)
|
||
{
|
||
if (*(table[i].values) == NULL)
|
||
{
|
||
/* @GRADIENT_NAME */
|
||
grad_name = (GradientName *) ((char*) gflare + table[i].name_offset);
|
||
gradient = *(table[i].values) = g_new (guchar, 4 * GRADIENT_RESOLUTION);
|
||
gradient_get_values (*grad_name, gradient, GRADIENT_RESOLUTION);
|
||
|
||
/*
|
||
* Do hue rotation, if needed
|
||
*/
|
||
|
||
if (table[i].hue_offset != 0)
|
||
{
|
||
hue_deg = calc.hue + *(gdouble *) ((char*) gflare + table[i].hue_offset);
|
||
hue = (gint) (hue_deg / 360.0 * 256.0) % 256;
|
||
if (hue < 0)
|
||
hue += 256;
|
||
g_assert (0 <= hue && hue < 256);
|
||
|
||
if (hue > 0)
|
||
{
|
||
for (j = 0; j < GRADIENT_RESOLUTION; j++)
|
||
{
|
||
GimpRGB rgb;
|
||
GimpHSV hsv;
|
||
|
||
rgb.r = (gdouble) gradient[j*4] / 255.0;
|
||
rgb.g = (gdouble) gradient[j*4+1] / 255.0;
|
||
rgb.b = (gdouble) gradient[j*4+2] / 255.0;
|
||
|
||
gimp_rgb_to_hsv (&rgb, &hsv);
|
||
|
||
hsv.h = (hsv.h + ((gdouble) hue / 255.0));
|
||
if (hsv.h > 1.0)
|
||
hsv.h -= 1.0;
|
||
|
||
gimp_hsv_to_rgb (&hsv, &rgb);
|
||
|
||
gradient[j*4] = ROUND (rgb.r * 255.0);
|
||
gradient[j*4+1] = ROUND (rgb.g * 255.0);
|
||
gradient[j*4+2] = ROUND (rgb.b * 255.0);
|
||
}
|
||
}
|
||
}
|
||
|
||
/*
|
||
* Grayfy gradient, if needed
|
||
*/
|
||
|
||
if (table[i].gray)
|
||
{
|
||
for (j = 0; j < GRADIENT_RESOLUTION; j++)
|
||
/* the first byte is enough */
|
||
gradient[j*4] = LUMINOSITY ((gradient + j*4));
|
||
|
||
}
|
||
|
||
/* sampling of one gradient is done */
|
||
return TRUE;
|
||
}
|
||
}
|
||
return FALSE;
|
||
}
|
||
|
||
static void
|
||
calc_place_sflare (void)
|
||
{
|
||
GFlare *gflare;
|
||
CalcSFlare *sflare;
|
||
gdouble prob[GRADIENT_RESOLUTION];
|
||
gdouble sum, sum2;
|
||
gdouble pos;
|
||
gdouble rnd, sizefac;
|
||
int n;
|
||
int i;
|
||
GRand *gr;
|
||
|
||
gr = g_rand_new ();
|
||
if ((calc.type & CALC_SFLARE) == 0)
|
||
return;
|
||
|
||
gflare = calc.gflare;
|
||
|
||
/*
|
||
Calc cumulative probability
|
||
*/
|
||
|
||
sum = 0.0;
|
||
for (i = 0; i < GRADIENT_RESOLUTION; i++)
|
||
{
|
||
/* probability gradient was grayfied already */
|
||
prob[i] = calc.sflare_probability[i*4];
|
||
sum += prob[i];
|
||
}
|
||
|
||
if (sum == 0.0)
|
||
sum = 1.0;
|
||
|
||
sum2 = 0;
|
||
for (i = 0; i < GRADIENT_RESOLUTION; i++)
|
||
{
|
||
sum2 += prob[i]; /* cumulation */
|
||
prob[i] = sum2 / sum;
|
||
}
|
||
|
||
g_rand_set_seed (gr, gflare->sflare_seed);
|
||
|
||
for (n = 0; n < SFLARE_NUM; n++)
|
||
{
|
||
sflare = g_new (CalcSFlare, 1);
|
||
rnd = g_rand_double (gr);
|
||
for (i = 0; i < GRADIENT_RESOLUTION; i++)
|
||
if (prob[i] >= rnd)
|
||
break;
|
||
if (i >= GRADIENT_RESOLUTION)
|
||
i = GRADIENT_RESOLUTION - 1;
|
||
|
||
/* sizefac gradient was grayfied already */
|
||
sizefac = calc.sflare_sizefac[i*4] / 255.0;
|
||
sizefac = pow (sizefac, 5.0);
|
||
|
||
pos = (double) (i - GRADIENT_RESOLUTION / 2) / GRADIENT_RESOLUTION;
|
||
sflare->xcenter = calc.xcenter + cos (calc.vangle) * calc.vlength * pos;
|
||
sflare->ycenter = calc.ycenter - sin (calc.vangle) * calc.vlength * pos;
|
||
sflare->radius = sizefac * calc.sflare_radius; /* FIXME */
|
||
sflare->bounds.x0 = sflare->xcenter - sflare->radius - 1;
|
||
sflare->bounds.x1 = sflare->xcenter + sflare->radius + 1;
|
||
sflare->bounds.y0 = sflare->ycenter - sflare->radius - 1;
|
||
sflare->bounds.y1 = sflare->ycenter + sflare->radius + 1;
|
||
calc.sflare_list = g_list_append (calc.sflare_list, sflare);
|
||
}
|
||
|
||
g_rand_free (gr);
|
||
}
|
||
|
||
static void
|
||
calc_deinit (void)
|
||
{
|
||
if (!calc.init)
|
||
{
|
||
g_warning("calc_deinit: not initialized");
|
||
return;
|
||
}
|
||
|
||
g_list_free_full (g_steal_pointer (&calc.sflare_list), (GDestroyNotify) g_free);
|
||
calc.sflare_list = NULL;
|
||
|
||
g_free (calc.glow_radial);
|
||
g_free (calc.glow_angular);
|
||
g_free (calc.glow_angular_size);
|
||
g_free (calc.rays_radial);
|
||
g_free (calc.rays_angular);
|
||
g_free (calc.rays_angular_size);
|
||
g_free (calc.sflare_radial);
|
||
g_free (calc.sflare_sizefac);
|
||
g_free (calc.sflare_probability);
|
||
|
||
calc.init = FALSE;
|
||
}
|
||
|
||
/*
|
||
* Get sample value at specified position of a gradient
|
||
*
|
||
* gradient samples are stored into array at the time of
|
||
* calc_sample_one_gradients (), and it is now linear interpolated.
|
||
*
|
||
* INPUT:
|
||
* guchar gradient[4*GRADIENT_RESOLUTION] gradient array(RGBA)
|
||
* gdouble pos position (0<=pos<=1)
|
||
* OUTPUT:
|
||
* guchar pix[4]
|
||
*/
|
||
static void
|
||
calc_get_gradient (guchar *pix, guchar *gradient, gdouble pos)
|
||
{
|
||
gint ipos;
|
||
gdouble frac;
|
||
gint i;
|
||
|
||
if (pos < 0 || pos > 1)
|
||
{
|
||
pix[0] = pix[1] = pix[2] = pix[3] = 0;
|
||
return;
|
||
}
|
||
pos *= GRADIENT_RESOLUTION - 1.0001;
|
||
ipos = (gint) pos; frac = pos - ipos;
|
||
gradient += ipos * 4;
|
||
|
||
for (i = 0; i < 4; i++)
|
||
{
|
||
pix[i] = gradient[i] * (1 - frac) + gradient[i+4] * frac;
|
||
}
|
||
}
|
||
|
||
/* I need fmod to return always positive value */
|
||
static gdouble
|
||
fmod_positive (gdouble x, gdouble m)
|
||
{
|
||
return x - floor (x/m) * m;
|
||
}
|
||
|
||
/*
|
||
* Calc glow's pixel (RGBA) value
|
||
* INPUT:
|
||
* gdouble x, y image coordinates
|
||
* OUTPUT:
|
||
* guchar pix[4]
|
||
*/
|
||
static void
|
||
calc_glow_pix (guchar *dest_pix, gdouble x, gdouble y)
|
||
{
|
||
gdouble radius, angle;
|
||
gdouble angular_size;
|
||
guchar radial_pix[4], angular_pix[4], size_pix[4];
|
||
gint i;
|
||
|
||
if ((calc.type & CALC_GLOW) == 0
|
||
|| x < calc.glow_bounds.x0 || x > calc.glow_bounds.x1
|
||
|| y < calc.glow_bounds.y0 || y > calc.glow_bounds.y1)
|
||
{
|
||
memset (dest_pix, 0, 4);
|
||
return;
|
||
}
|
||
|
||
x -= calc.xcenter;
|
||
y -= calc.ycenter;
|
||
radius = sqrt (x*x + y*y) / calc.glow_radius;
|
||
angle = (atan2 (-y, x) + calc.glow_rotation ) / (2 * G_PI);
|
||
angle = fmod_positive (angle, 1.0);
|
||
|
||
calc_get_gradient (size_pix, calc.glow_angular_size, angle);
|
||
/* angular_size gradient was grayfied already */
|
||
angular_size = size_pix[0] / 255.0;
|
||
radius /= (angular_size+0.0001); /* in case angular_size == 0.0 */
|
||
if (radius < 0 || radius > 1)
|
||
{
|
||
memset (dest_pix, 0, 4);
|
||
return;
|
||
}
|
||
|
||
calc_get_gradient (radial_pix, calc.glow_radial, radius);
|
||
calc_get_gradient (angular_pix, calc.glow_angular, angle);
|
||
|
||
for (i = 0; i < 4; i++)
|
||
dest_pix[i] = radial_pix[i] * angular_pix[i] / 255;
|
||
}
|
||
|
||
/*
|
||
* Calc rays's pixel (RGBA) value
|
||
*
|
||
*/
|
||
static void
|
||
calc_rays_pix (guchar *dest_pix, gdouble x, gdouble y)
|
||
{
|
||
gdouble radius, angle;
|
||
gdouble angular_size;
|
||
gdouble spike_frac, spike_inten, spike_angle;
|
||
guchar radial_pix[4], angular_pix[4], size_pix[4];
|
||
gint i;
|
||
|
||
if ((calc.type & CALC_RAYS) == 0
|
||
|| x < calc.rays_bounds.x0 || x > calc.rays_bounds.x1
|
||
|| y < calc.rays_bounds.y0 || y > calc.rays_bounds.y1)
|
||
{
|
||
memset (dest_pix, 0, 4);
|
||
return;
|
||
}
|
||
|
||
x -= calc.xcenter;
|
||
y -= calc.ycenter;
|
||
radius = sqrt (x*x + y*y) / calc.rays_radius;
|
||
angle = (atan2 (-y, x) + calc.rays_rotation ) / (2 * G_PI);
|
||
angle = fmod_positive (angle, 1.0); /* make sure 0 <= angle < 1.0 */
|
||
spike_frac = fmod (angle, calc.rays_spike_mod * 2);
|
||
spike_angle = angle - spike_frac + calc.rays_spike_mod;
|
||
spike_frac = (angle - spike_angle) / calc.rays_spike_mod;
|
||
/* spike_frac is between -1.0 and 1.0 here (except round error...) */
|
||
|
||
spike_inten = pow (1.0 - fabs (spike_frac), calc.rays_thinness);
|
||
|
||
calc_get_gradient (size_pix, calc.rays_angular_size, spike_angle);
|
||
/* angular_size gradient was grayfied already */
|
||
angular_size = size_pix[0] / 255.0;
|
||
radius /= (angular_size+0.0001); /* in case angular_size == 0.0 */
|
||
if(radius < 0 || radius > 1)
|
||
{
|
||
memset (dest_pix, 0, 4);
|
||
return;
|
||
}
|
||
|
||
calc_get_gradient (radial_pix, calc.rays_radial, radius);
|
||
calc_get_gradient (angular_pix, calc.rays_angular, spike_angle);
|
||
|
||
for (i = 0; i < 3; i++)
|
||
dest_pix[i] = radial_pix[i] * angular_pix[i] / 255;
|
||
dest_pix[3] = spike_inten * radial_pix[3] * angular_pix[3] / 255;
|
||
}
|
||
|
||
/*
|
||
* Calc sflare's pixel (RGBA) value
|
||
*
|
||
* the sflare (second flares) are needed to be rendered one each
|
||
* sequentially, onto the source image, such as like usual layer
|
||
* operations. So the function takes src_pix as argument. glow, rays
|
||
* routines don't have src_pix as argument, because of convenience.
|
||
*
|
||
* @JAPANESE
|
||
* sflare $B$OJ#?t$N%U%l%"$r=g$K(B($B%l%$%dE*$K(B)$B$+$V$;$J$,$iIA2h$9$kI,MW$,(B
|
||
* $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
|
||
* glow, rays $B$O4J0W2=$N$?$a$K$J$7!#(B
|
||
*/
|
||
void
|
||
calc_sflare_pix (guchar *dest_pix,
|
||
gdouble x,
|
||
gdouble y,
|
||
guchar *src_pix)
|
||
{
|
||
GList *list;
|
||
CalcSFlare *sflare;
|
||
gdouble sx, sy, th;
|
||
gdouble radius, angle;
|
||
guchar radial_pix[4], tmp_pix[4];
|
||
|
||
memcpy (dest_pix, src_pix, 4);
|
||
|
||
if ((calc.type & CALC_SFLARE) == 0)
|
||
return;
|
||
|
||
list = calc.sflare_list;
|
||
|
||
while (list)
|
||
{
|
||
sflare = list->data;
|
||
list = list->next;
|
||
|
||
if (! sflare)
|
||
break;
|
||
|
||
if (x < sflare->bounds.x0 || x > sflare->bounds.x1
|
||
|| y < sflare->bounds.y0 || y > sflare->bounds.y1)
|
||
continue;
|
||
sx = x - sflare->xcenter;
|
||
sy = y - sflare->ycenter;
|
||
radius = sqrt (sx * sx + sy * sy) / sflare->radius;
|
||
if (calc.sflare_shape == GF_POLYGON)
|
||
{
|
||
angle = atan2 (-sy, sx) - calc.vangle + calc.sflare_rotation;
|
||
th = fmod_positive (angle, calc.sflare_angle * 2) - calc.sflare_angle;
|
||
radius *= cos (th) * calc.sflare_factor;
|
||
}
|
||
if (radius < 0 || radius > 1)
|
||
continue;
|
||
|
||
calc_get_gradient (radial_pix, calc.sflare_radial, radius);
|
||
memcpy (tmp_pix, dest_pix, 4);
|
||
calc_paint_func (dest_pix, tmp_pix, radial_pix,
|
||
calc.sflare_opacity, calc.gflare->sflare_mode);
|
||
}
|
||
}
|
||
|
||
static void
|
||
calc_gflare_pix (guchar *dest_pix, gdouble x, gdouble y, guchar *src_pix)
|
||
{
|
||
GFlare *gflare = calc.gflare;
|
||
guchar glow_pix[4], rays_pix[4];
|
||
guchar tmp_pix[4];
|
||
|
||
memcpy (dest_pix, src_pix, 4);
|
||
|
||
if (calc.type & CALC_GLOW)
|
||
{
|
||
memcpy (tmp_pix, dest_pix, 4);
|
||
calc_glow_pix (glow_pix, x, y);
|
||
calc_paint_func (dest_pix, tmp_pix, glow_pix,
|
||
calc.glow_opacity, gflare->glow_mode);
|
||
}
|
||
if (calc.type & CALC_RAYS)
|
||
{
|
||
memcpy (tmp_pix, dest_pix, 4);
|
||
calc_rays_pix (rays_pix, x, y);
|
||
calc_paint_func (dest_pix, tmp_pix, rays_pix,
|
||
calc.rays_opacity, gflare->rays_mode);
|
||
}
|
||
if (calc.type & CALC_SFLARE)
|
||
{
|
||
memcpy (tmp_pix, dest_pix, 4);
|
||
calc_sflare_pix (dest_pix, x, y, tmp_pix);
|
||
}
|
||
}
|
||
|
||
/*
|
||
Paint func routines, such as Normal, Addition, ...
|
||
*/
|
||
static void
|
||
calc_paint_func (guchar *dest, guchar *src1, guchar *src2, gint opacity,
|
||
GFlareMode mode)
|
||
{
|
||
guchar buf[4], *s=buf;
|
||
|
||
if (src2[3] == 0 || opacity <= 0)
|
||
{
|
||
memcpy (dest, src1, 4);
|
||
return;
|
||
}
|
||
|
||
switch (mode)
|
||
{
|
||
case GF_NORMAL:
|
||
s = src2;
|
||
break;
|
||
case GF_ADDITION:
|
||
calc_addition (s, src1, src2);
|
||
break;
|
||
case GF_OVERLAY:
|
||
calc_overlay (s, src1, src2);
|
||
break;
|
||
case GF_SCREEN:
|
||
calc_screen (s, src1, src2);
|
||
break;
|
||
default:
|
||
s = src2;
|
||
break;
|
||
}
|
||
calc_combine (dest, src1, s, opacity);
|
||
}
|
||
|
||
static void
|
||
calc_combine (guchar *dest, guchar *src1, guchar *src2, gint opacity)
|
||
{
|
||
gdouble s1_a, s2_a, new_a;
|
||
gdouble ratio, compl_ratio;
|
||
gint i;
|
||
|
||
s1_a = src1[3] / 255.0;
|
||
s2_a = src2[3] * opacity / 65025.0;
|
||
new_a = s1_a + (1.0 - s1_a) * s2_a;
|
||
|
||
if (new_a != 0.0)
|
||
ratio = s2_a / new_a;
|
||
else
|
||
ratio = 0.0;
|
||
|
||
compl_ratio = 1.0 - ratio;
|
||
|
||
for (i = 0; i < 3; i++)
|
||
dest[i] = src1[i] * compl_ratio + src2[i] * ratio;
|
||
|
||
dest[3] = new_a * 255.0;
|
||
}
|
||
|
||
static void
|
||
calc_addition (guchar *dest, guchar *src1, guchar *src2)
|
||
{
|
||
gint tmp, i;
|
||
|
||
for (i = 0; i < 3; i++)
|
||
{
|
||
tmp = src1[i] + src2[i];
|
||
dest[i] = tmp <= 255 ? tmp: 255;
|
||
}
|
||
dest[3] = MIN (src1[3], src2[3]);
|
||
}
|
||
|
||
static void
|
||
calc_screen (guchar *dest, guchar *src1, guchar *src2)
|
||
{
|
||
gint i;
|
||
|
||
for (i = 0; i < 3; i++)
|
||
{
|
||
dest[i] = 255 - ((255 - src1[i]) * (255 - src2[i])) / 255;
|
||
}
|
||
dest[3] = MIN (src1[3], src2[3]);
|
||
}
|
||
|
||
static void
|
||
calc_overlay (guchar *dest, guchar *src1, guchar *src2)
|
||
{
|
||
gint screen, mult, i;
|
||
|
||
for (i = 0; i < 3; i++)
|
||
{
|
||
screen = 255 - ((255 - src1[i]) * (255 - src2[i])) / 255;
|
||
mult = (src1[i] * src2[i]) / 255;
|
||
dest[i] = (screen * src1[i] + mult * (255 - src1[i])) / 255;
|
||
}
|
||
dest[3] = MIN (src1[3], src2[3]);
|
||
}
|
||
|
||
/*************************************************************************/
|
||
/** **/
|
||
/** Main Dialog **/
|
||
/** +++ dlg **/
|
||
/** **/
|
||
/*************************************************************************/
|
||
|
||
/*
|
||
This is gflare main dialog, one which opens in first.
|
||
*/
|
||
|
||
static gboolean
|
||
dlg_run (GimpProcedure *procedure,
|
||
GimpProcedureConfig *config)
|
||
{
|
||
GeglBuffer *src_buffer;
|
||
GtkWidget *shell;
|
||
GtkWidget *hbox;
|
||
GtkWidget *vbox;
|
||
GtkWidget *frame;
|
||
GtkWidget *button;
|
||
GtkWidget *notebook;
|
||
gboolean run = FALSE;
|
||
gchar *gflare_name;
|
||
|
||
gimp_ui_init (PLUG_IN_BINARY);
|
||
|
||
/*
|
||
* Init Main Dialog
|
||
*/
|
||
|
||
dlg = g_new0 (GFlareDialog, 1);
|
||
dlg->init = TRUE;
|
||
dlg->update_preview = TRUE;
|
||
|
||
gradient_menu_init (); /* FIXME: this should go elsewhere */
|
||
g_object_get (config, "gflare-name", &gflare_name, NULL);
|
||
dlg_setup_gflare (gflare_name);
|
||
g_free (gflare_name);
|
||
|
||
g_assert (gflares_list != NULL);
|
||
g_assert (dlg->gflare != NULL);
|
||
g_assert (dlg->gflare->name != NULL);
|
||
|
||
/*
|
||
* Dialog Shell
|
||
*/
|
||
|
||
shell = dlg->shell = gimp_procedure_dialog_new (procedure,
|
||
GIMP_PROCEDURE_CONFIG (config),
|
||
_("Gradient Flare"));
|
||
|
||
/*
|
||
* main hbox
|
||
*/
|
||
|
||
hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
|
||
gtk_container_set_border_width (GTK_CONTAINER (hbox), 12);
|
||
gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (shell))),
|
||
hbox, FALSE, FALSE, 0);
|
||
gtk_widget_show (hbox);
|
||
|
||
/*
|
||
* Preview
|
||
*/
|
||
|
||
vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
|
||
gtk_box_pack_start (GTK_BOX (hbox), vbox, FALSE, FALSE, 0);
|
||
gtk_widget_show (vbox);
|
||
|
||
frame = gtk_frame_new (NULL);
|
||
gtk_widget_set_valign (frame, GTK_ALIGN_START);
|
||
gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
|
||
gtk_box_pack_start (GTK_BOX (vbox), frame, TRUE, TRUE, 0);
|
||
gtk_widget_show (frame);
|
||
|
||
src_buffer = gimp_drawable_get_buffer (drawable);
|
||
|
||
dlg->preview = preview_new (DLG_PREVIEW_WIDTH, DLG_PREVIEW_HEIGHT,
|
||
dlg_preview_init_func, config,
|
||
dlg_preview_render_func, src_buffer,
|
||
dlg_preview_deinit_func, NULL);
|
||
gtk_widget_set_events (GTK_WIDGET (dlg->preview->widget), DLG_PREVIEW_MASK);
|
||
gtk_container_add (GTK_CONTAINER (frame), dlg->preview->widget);
|
||
|
||
g_signal_connect (dlg->preview->widget, "realize",
|
||
G_CALLBACK (dlg_preview_realize),
|
||
NULL);
|
||
g_signal_connect (dlg->preview->widget, "event",
|
||
G_CALLBACK (dlg_preview_handle_event),
|
||
config);
|
||
|
||
dlg_preview_calc_window ();
|
||
|
||
button = gtk_check_button_new_with_mnemonic (_("A_uto update preview"));
|
||
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button),
|
||
dlg->update_preview);
|
||
gtk_box_pack_end (GTK_BOX (vbox), button, FALSE, FALSE, 0);
|
||
gtk_widget_show (button);
|
||
|
||
g_signal_connect (button, "toggled",
|
||
G_CALLBACK (dlg_update_preview_callback),
|
||
&dlg->update_preview);
|
||
|
||
/*
|
||
* Notebook
|
||
*/
|
||
|
||
notebook = dlg->notebook = gtk_notebook_new ();
|
||
gtk_box_pack_start (GTK_BOX (hbox), notebook, TRUE, TRUE, 0);
|
||
gtk_widget_show (notebook);
|
||
|
||
dlg_make_page_settings (dlg, notebook, config);
|
||
dlg_make_page_selector (dlg, notebook, config);
|
||
|
||
gtk_widget_show (shell);
|
||
|
||
/*
|
||
* Initialization done
|
||
*/
|
||
dlg->init = FALSE;
|
||
dlg_preview_update ();
|
||
|
||
if (gimp_dialog_run (GIMP_DIALOG (shell)) == GTK_RESPONSE_OK)
|
||
{
|
||
g_object_set (config,
|
||
"gflare-name", dlg->gflare->name,
|
||
NULL);
|
||
|
||
run = TRUE;
|
||
}
|
||
|
||
g_object_unref (src_buffer);
|
||
|
||
gtk_widget_destroy (shell);
|
||
|
||
return run;
|
||
}
|
||
|
||
static void
|
||
dlg_setup_gflare (const gchar *gflare_name)
|
||
{
|
||
dlg->gflare = gflares_list_lookup (gflare_name);
|
||
|
||
if (!dlg->gflare)
|
||
{
|
||
dlg->gflare = gflares_list_lookup ("Default");
|
||
if (!dlg->gflare)
|
||
{
|
||
g_warning (_("'Default' is created."));
|
||
dlg->gflare = gflare_new_with_default (_("Default"));
|
||
gflares_list_insert (dlg->gflare);
|
||
}
|
||
}
|
||
}
|
||
|
||
/***********************************/
|
||
/** Main Dialog / Preview **/
|
||
/***********************************/
|
||
|
||
/*
|
||
* Calculate preview's window, ie. translation of preview widget and
|
||
* drawable.
|
||
*
|
||
* x0, x1, y0, y1 are drawable coord, corresponding with top left
|
||
* corner of preview widget, etc.
|
||
*/
|
||
void
|
||
dlg_preview_calc_window (void)
|
||
{
|
||
gint width = gimp_drawable_get_width (drawable);
|
||
gint height = gimp_drawable_get_height (drawable);
|
||
gint is_wide;
|
||
gdouble offx, offy;
|
||
|
||
is_wide = ((double) DLG_PREVIEW_HEIGHT * width >=
|
||
(double) DLG_PREVIEW_WIDTH * height);
|
||
|
||
if (is_wide)
|
||
{
|
||
offy = ((double) width * DLG_PREVIEW_HEIGHT / DLG_PREVIEW_WIDTH) / 2.0;
|
||
|
||
dlg->pwin.x0 = 0;
|
||
dlg->pwin.x1 = width;
|
||
dlg->pwin.y0 = height / 2.0 - offy;
|
||
dlg->pwin.y1 = height / 2.0 + offy;
|
||
}
|
||
else
|
||
{
|
||
offx = ((double) height * DLG_PREVIEW_WIDTH / DLG_PREVIEW_HEIGHT) / 2.0;
|
||
|
||
dlg->pwin.x0 = width / 2.0 - offx;
|
||
dlg->pwin.x1 = width / 2.0 + offx;
|
||
dlg->pwin.y0 = 0;
|
||
dlg->pwin.y1 = height;
|
||
}
|
||
}
|
||
|
||
void
|
||
ed_preview_calc_window (void)
|
||
{
|
||
gint width = gimp_drawable_get_width (drawable);
|
||
gint height = gimp_drawable_get_height (drawable);
|
||
gint is_wide;
|
||
gdouble offx, offy;
|
||
|
||
is_wide = ((double) DLG_PREVIEW_HEIGHT * width >=
|
||
(double) DLG_PREVIEW_WIDTH * height);
|
||
|
||
if (is_wide)
|
||
{
|
||
offy = ((double) width * DLG_PREVIEW_HEIGHT / DLG_PREVIEW_WIDTH) / 2.0;
|
||
|
||
dlg->pwin.x0 = 0;
|
||
dlg->pwin.x1 = width;
|
||
dlg->pwin.y0 = height / 2.0 - offy;
|
||
dlg->pwin.y1 = height / 2.0 + offy;
|
||
}
|
||
else
|
||
{
|
||
offx = ((double) height * DLG_PREVIEW_WIDTH / DLG_PREVIEW_HEIGHT) / 2.0;
|
||
|
||
dlg->pwin.x0 = width / 2.0 - offx; dlg->pwin.x1 = width / 2.0 + offx;
|
||
dlg->pwin.y0 = 0;
|
||
dlg->pwin.y1 = height;
|
||
}
|
||
}
|
||
|
||
static void
|
||
dlg_preview_realize (GtkWidget *widget)
|
||
{
|
||
GdkDisplay *display = gtk_widget_get_display (widget);
|
||
GdkCursor *cursor = gdk_cursor_new_for_display (display, GDK_CROSSHAIR);
|
||
|
||
gdk_window_set_cursor (gtk_widget_get_window (widget), cursor);
|
||
g_object_unref (cursor);
|
||
}
|
||
|
||
static gboolean
|
||
dlg_preview_handle_event (GtkWidget *widget,
|
||
GdkEvent *event,
|
||
GimpProcedureConfig *config)
|
||
{
|
||
GdkEventButton *bevent;
|
||
gint bx, by, x, y;
|
||
gint xcenter;
|
||
gint ycenter;
|
||
gboolean handled = FALSE;
|
||
|
||
g_object_get (config,
|
||
"center-x", &xcenter,
|
||
"center-y", &ycenter,
|
||
NULL);
|
||
|
||
switch (event->type)
|
||
{
|
||
case GDK_BUTTON_PRESS:
|
||
bevent = (GdkEventButton *) event;
|
||
bx = bevent->x;
|
||
by = bevent->y;
|
||
|
||
/* convert widget coord to drawable coord */
|
||
x = dlg->pwin.x0 + (double) (dlg->pwin.x1 - dlg->pwin.x0)
|
||
* bx / DLG_PREVIEW_WIDTH;
|
||
y = dlg->pwin.y0 + (double) (dlg->pwin.y1 - dlg->pwin.y0)
|
||
* by / DLG_PREVIEW_HEIGHT;
|
||
|
||
if ((x != xcenter || y != ycenter))
|
||
{
|
||
if (x != xcenter)
|
||
{
|
||
xcenter = x;
|
||
gimp_size_entry_set_refval (GIMP_SIZE_ENTRY (dlg->sizeentry),
|
||
0, x);
|
||
}
|
||
if (y != ycenter)
|
||
{
|
||
ycenter = y;
|
||
gimp_size_entry_set_refval (GIMP_SIZE_ENTRY (dlg->sizeentry),
|
||
1, y);
|
||
}
|
||
dlg_preview_update ();
|
||
}
|
||
handled = TRUE;
|
||
break;
|
||
default:
|
||
break;
|
||
}
|
||
|
||
g_object_set (config,
|
||
"center-x", xcenter,
|
||
"center-y", ycenter,
|
||
NULL);
|
||
|
||
return handled;
|
||
}
|
||
|
||
static void
|
||
dlg_preview_update (void)
|
||
{
|
||
if (!dlg->init && dlg->update_preview)
|
||
{
|
||
dlg->init_params_done = FALSE;
|
||
preview_render_start (dlg->preview);
|
||
}
|
||
}
|
||
|
||
/* preview callbacks */
|
||
static gint
|
||
dlg_preview_init_func (Preview *preview,
|
||
gpointer data)
|
||
{
|
||
GimpProcedureConfig *config = GIMP_PROCEDURE_CONFIG (data);
|
||
|
||
/* call init_params first, and iterate init_progress while
|
||
it returns true */
|
||
if (dlg->init_params_done == FALSE)
|
||
{
|
||
gint xcenter;
|
||
gint ycenter;
|
||
gdouble radius;
|
||
gdouble rotation;
|
||
gdouble hue;
|
||
gdouble vangle;
|
||
gdouble vlength;
|
||
|
||
g_object_get (config,
|
||
"center-x", &xcenter,
|
||
"center-y", &ycenter,
|
||
"radius", &radius,
|
||
"rotation", &rotation,
|
||
"hue", &hue,
|
||
"vector-angle", &vangle,
|
||
"vector-length", &vlength,
|
||
NULL);
|
||
|
||
calc_init_params (dlg->gflare,
|
||
CALC_GLOW | CALC_RAYS | CALC_SFLARE,
|
||
xcenter, ycenter,
|
||
radius, rotation, hue,
|
||
vangle, vlength);
|
||
dlg->init_params_done = TRUE;
|
||
return TRUE;
|
||
}
|
||
return calc_init_progress ();
|
||
}
|
||
|
||
/* render preview
|
||
do what "preview" means, ie. render lense flare effect onto drawable */
|
||
static void
|
||
dlg_preview_render_func (Preview *preview,
|
||
guchar *dest,
|
||
gint y,
|
||
gpointer data)
|
||
{
|
||
GeglBuffer *src_buffer = data;
|
||
gint width = gimp_drawable_get_width (drawable);
|
||
gint height = gimp_drawable_get_height (drawable);
|
||
gint x;
|
||
gint dx, dy; /* drawable x, y */
|
||
guchar *src_row, *src;
|
||
guchar src_pix[4], dest_pix[4];
|
||
gint b;
|
||
|
||
dy = (dlg->pwin.y0 +
|
||
(gdouble) (dlg->pwin.y1 - dlg->pwin.y0) * y / DLG_PREVIEW_HEIGHT);
|
||
|
||
if (dy < 0 || dy >= height)
|
||
{
|
||
memset (dest, GRAY50, 3 * DLG_PREVIEW_WIDTH);
|
||
return;
|
||
}
|
||
|
||
src_row = g_new (guchar, dinfo.bpp * width);
|
||
|
||
gegl_buffer_get (src_buffer, GEGL_RECTANGLE (0, dy, width, 1), 1.0,
|
||
dinfo.format, src_row,
|
||
GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
|
||
|
||
for (x = 0; x < DLG_PREVIEW_HEIGHT; x++)
|
||
{
|
||
dx = (dlg->pwin.x0 +
|
||
(double) (dlg->pwin.x1 - dlg->pwin.x0) * x / DLG_PREVIEW_WIDTH);
|
||
|
||
if (dx < 0 || dx >= width)
|
||
{
|
||
for (b = 0; b < 3; b++)
|
||
*dest++ = GRAY50;
|
||
continue;
|
||
}
|
||
|
||
/* Get drawable pix value */
|
||
src = &src_row[dx * dinfo.bpp];
|
||
|
||
for (b = 0; b < 3; b++)
|
||
src_pix[b] = dinfo.is_color ? src[b] : src[0];
|
||
src_pix[3] = dinfo.has_alpha ? src[dinfo.bpp - 1] : OPAQUE;
|
||
|
||
/* Get GFlare pix value */
|
||
|
||
calc_gflare_pix (dest_pix, dx, dy, src_pix);
|
||
|
||
/* Draw gray check if needed */
|
||
preview_rgba_to_rgb (dest, x, y, dest_pix);
|
||
dest += 3;
|
||
}
|
||
|
||
g_free (src_row);
|
||
}
|
||
|
||
static void
|
||
dlg_preview_deinit_func (Preview *preview, gpointer data)
|
||
{
|
||
if (dlg->init_params_done)
|
||
{
|
||
calc_deinit ();
|
||
dlg->init_params_done = FALSE;
|
||
}
|
||
}
|
||
|
||
/*****************************************/
|
||
/** Main Dialog / Settings Page **/
|
||
/*****************************************/
|
||
|
||
static void
|
||
dlg_make_page_settings (GFlareDialog *dlg,
|
||
GtkWidget *notebook,
|
||
GimpProcedureConfig *config)
|
||
{
|
||
GtkWidget *main_vbox;
|
||
GtkWidget *frame;
|
||
GtkWidget *center;
|
||
GtkWidget *chain;
|
||
GtkWidget *scale;
|
||
gdouble xres, yres;
|
||
gint xcenter;
|
||
gint ycenter;
|
||
|
||
g_object_get (config,
|
||
"center-x", &xcenter,
|
||
"center-y", &ycenter,
|
||
NULL);
|
||
gimp_image_get_resolution (image, &xres, &yres);
|
||
|
||
center = dlg->sizeentry =
|
||
gimp_coordinates_new (gimp_image_get_unit (image), "%a",
|
||
TRUE, TRUE, 75, GIMP_SIZE_ENTRY_UPDATE_SIZE,
|
||
|
||
FALSE, FALSE,
|
||
|
||
_("_X:"), xcenter, xres,
|
||
-GIMP_MAX_IMAGE_SIZE, GIMP_MAX_IMAGE_SIZE,
|
||
0, gimp_drawable_get_width (drawable),
|
||
|
||
_("_Y:"), ycenter, yres,
|
||
-GIMP_MAX_IMAGE_SIZE, GIMP_MAX_IMAGE_SIZE,
|
||
0, gimp_drawable_get_height (drawable));
|
||
|
||
chain = GTK_WIDGET (GIMP_COORDINATES_CHAINBUTTON (center));
|
||
|
||
g_signal_connect (center, "value-changed",
|
||
G_CALLBACK (dlg_position_entry_callback),
|
||
config);
|
||
g_signal_connect (center, "refval-changed",
|
||
G_CALLBACK (dlg_position_entry_callback),
|
||
config);
|
||
gtk_widget_hide (chain);
|
||
gtk_widget_show (center);
|
||
|
||
gimp_procedure_dialog_get_label (GIMP_PROCEDURE_DIALOG (dlg->shell),
|
||
"center-title", _("Center"),
|
||
FALSE, FALSE);
|
||
frame = gimp_procedure_dialog_fill_frame (GIMP_PROCEDURE_DIALOG (dlg->shell),
|
||
"center-frame", "center-title",
|
||
FALSE, NULL);
|
||
gtk_container_add (GTK_CONTAINER (frame), center);
|
||
|
||
scale = gimp_procedure_dialog_get_spin_scale (GIMP_PROCEDURE_DIALOG (dlg->shell), "radius", 1.0);
|
||
gimp_spin_scale_set_scale_limits (GIMP_SPIN_SCALE (scale), 1.0,
|
||
gimp_drawable_get_width (drawable) / 2);
|
||
|
||
gimp_procedure_dialog_get_spin_scale (GIMP_PROCEDURE_DIALOG (dlg->shell), "rotation", 1.0);
|
||
gimp_procedure_dialog_get_spin_scale (GIMP_PROCEDURE_DIALOG (dlg->shell), "hue", 1.0);
|
||
gimp_procedure_dialog_get_spin_scale (GIMP_PROCEDURE_DIALOG (dlg->shell), "vector-angle", 1.0);
|
||
|
||
scale = gimp_procedure_dialog_get_spin_scale (GIMP_PROCEDURE_DIALOG (dlg->shell), "vector-length", 1.0);
|
||
gimp_spin_scale_set_scale_limits (GIMP_SPIN_SCALE (scale), 1.0, 1000);
|
||
|
||
gimp_procedure_dialog_fill_box (GIMP_PROCEDURE_DIALOG (dlg->shell),
|
||
"parameters-box",
|
||
"radius", "rotation", "hue",
|
||
"vector-angle", "vector-length",
|
||
NULL);
|
||
gimp_procedure_dialog_get_label (GIMP_PROCEDURE_DIALOG (dlg->shell),
|
||
"parameters-title", _("Parameters"),
|
||
FALSE, FALSE);
|
||
gimp_procedure_dialog_fill_frame (GIMP_PROCEDURE_DIALOG (dlg->shell),
|
||
"parameters-frame", "parameters-title",
|
||
FALSE, "parameters-box");
|
||
|
||
g_signal_connect (config, "notify",
|
||
G_CALLBACK (dlg_preview_update),
|
||
NULL);
|
||
|
||
/**
|
||
*** Asupsample settings
|
||
*** This code is stolen from gimp-0.99.x/app/blend.c
|
||
**/
|
||
|
||
gimp_procedure_dialog_get_spin_scale (GIMP_PROCEDURE_DIALOG (dlg->shell),
|
||
"asupsample-max-depth", 1.0);
|
||
|
||
gimp_procedure_dialog_get_spin_scale (GIMP_PROCEDURE_DIALOG (dlg->shell),
|
||
"asupsample-threshold", 1.0);
|
||
|
||
gimp_procedure_dialog_fill_box (GIMP_PROCEDURE_DIALOG (dlg->shell),
|
||
"asupsample-box",
|
||
"asupsample-max-depth", "asupsample-threshold",
|
||
NULL);
|
||
gimp_procedure_dialog_fill_frame (GIMP_PROCEDURE_DIALOG (dlg->shell),
|
||
"asupsample-frame", "use-asupsample",
|
||
FALSE, "asupsample-box");
|
||
|
||
main_vbox = gimp_procedure_dialog_fill_box (GIMP_PROCEDURE_DIALOG (dlg->shell),
|
||
"settings-page",
|
||
"center-frame", "parameters-frame", "asupsample-frame",
|
||
NULL);
|
||
gtk_notebook_append_page (GTK_NOTEBOOK (notebook), main_vbox,
|
||
gtk_label_new_with_mnemonic (_("_Settings")));
|
||
gtk_widget_show (main_vbox);
|
||
}
|
||
|
||
static void
|
||
dlg_position_entry_callback (GtkWidget *widget,
|
||
GimpProcedureConfig *config)
|
||
{
|
||
gint xcenter;
|
||
gint ycenter;
|
||
gint x, y;
|
||
|
||
x = RINT (gimp_size_entry_get_refval (GIMP_SIZE_ENTRY (widget), 0));
|
||
y = RINT (gimp_size_entry_get_refval (GIMP_SIZE_ENTRY (widget), 1));
|
||
|
||
DEBUG_PRINT (("dlg_position_entry_callback\n"));
|
||
|
||
g_object_get (config,
|
||
"center-x", &xcenter,
|
||
"center-y", &ycenter,
|
||
NULL);
|
||
|
||
if (xcenter != x || ycenter != y)
|
||
{
|
||
g_object_set (config,
|
||
"center-x", x,
|
||
"center-y", y,
|
||
NULL);
|
||
|
||
dlg_preview_update ();
|
||
}
|
||
}
|
||
|
||
static void
|
||
dlg_update_preview_callback (GtkWidget *widget,
|
||
gpointer data)
|
||
{
|
||
gimp_toggle_button_update (widget, data);
|
||
|
||
dlg_preview_update ();
|
||
}
|
||
|
||
/*****************************************/
|
||
/** Main Dialog / Selector Page **/
|
||
/*****************************************/
|
||
|
||
static void
|
||
dlg_make_page_selector (GFlareDialog *dlg,
|
||
GtkWidget *notebook,
|
||
GimpProcedureConfig *config)
|
||
{
|
||
GtkWidget *vbox;
|
||
GtkWidget *hbox;
|
||
GtkWidget *listbox;
|
||
GtkListStore *list;
|
||
GtkWidget *listview;
|
||
GtkCellRenderer *renderer;
|
||
GtkTreeViewColumn *column;
|
||
GtkWidget *button;
|
||
gint i;
|
||
|
||
static struct
|
||
{
|
||
const gchar *label;
|
||
GCallback callback;
|
||
}
|
||
buttons[] =
|
||
{
|
||
{ N_("_New"), G_CALLBACK (dlg_selector_new_callback) },
|
||
{ N_("_Edit"), G_CALLBACK (dlg_selector_edit_callback) },
|
||
{ N_("_Copy"), G_CALLBACK (dlg_selector_copy_callback) },
|
||
{ N_("_Delete"), G_CALLBACK (dlg_selector_delete_callback) }
|
||
};
|
||
|
||
DEBUG_PRINT (("dlg_make_page_selector\n"));
|
||
|
||
vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
|
||
gtk_container_set_border_width (GTK_CONTAINER (vbox), 12);
|
||
|
||
/*
|
||
* List Box
|
||
*/
|
||
|
||
listbox = gtk_scrolled_window_new (NULL, NULL);
|
||
gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (listbox),
|
||
GTK_POLICY_AUTOMATIC,
|
||
GTK_POLICY_AUTOMATIC);
|
||
|
||
gtk_widget_set_size_request (listbox, DLG_LISTBOX_WIDTH, DLG_LISTBOX_HEIGHT);
|
||
gtk_box_pack_start (GTK_BOX (vbox), listbox, TRUE, TRUE, 0);
|
||
|
||
list = dlg->selector_list = gtk_list_store_new (2,
|
||
G_TYPE_STRING,
|
||
G_TYPE_POINTER);
|
||
listview = gtk_tree_view_new_with_model (GTK_TREE_MODEL (list));
|
||
gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (listview), FALSE);
|
||
|
||
gtk_container_add (GTK_CONTAINER (listbox), listview);
|
||
gtk_widget_show (listbox);
|
||
dlg->selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (listview));
|
||
gtk_tree_selection_set_mode (dlg->selection, GTK_SELECTION_BROWSE);
|
||
gtk_widget_show (listview);
|
||
g_signal_connect (dlg->selection, "changed",
|
||
G_CALLBACK (dlg_selector_list_item_callback),
|
||
NULL);
|
||
|
||
renderer = gtk_cell_renderer_text_new ();
|
||
|
||
/* Note: the title isn't shown, so it doesn't need to be translated. */
|
||
column = gtk_tree_view_column_new_with_attributes ("GFlare", renderer,
|
||
"text", 0,
|
||
NULL);
|
||
gtk_tree_view_append_column (GTK_TREE_VIEW (listview), column);
|
||
|
||
dlg_selector_setup_listbox ();
|
||
|
||
/*
|
||
* The buttons for the possible listbox operations
|
||
*/
|
||
|
||
hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
|
||
gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
|
||
gtk_widget_show (hbox);
|
||
|
||
for (i = 0; i < G_N_ELEMENTS (buttons); i++)
|
||
{
|
||
button = gtk_button_new_with_mnemonic (gettext (buttons[i].label));
|
||
gtk_box_pack_start (GTK_BOX (hbox), button, TRUE, TRUE, 0);
|
||
gtk_widget_show (button);
|
||
|
||
g_signal_connect (button, "clicked",
|
||
buttons[i].callback,
|
||
config);
|
||
}
|
||
|
||
gtk_widget_show (vbox);
|
||
|
||
gtk_notebook_append_page (GTK_NOTEBOOK (notebook), vbox,
|
||
gtk_label_new_with_mnemonic (_("S_elector")));
|
||
gtk_widget_show (vbox);
|
||
}
|
||
|
||
/*
|
||
* Set up selector's listbox, according to gflares_list
|
||
*/
|
||
static void
|
||
dlg_selector_setup_listbox (void)
|
||
{
|
||
GList *list;
|
||
GFlare *gflare;
|
||
gint n;
|
||
|
||
list = gflares_list;
|
||
n = 0;
|
||
|
||
while (list)
|
||
{
|
||
GtkTreeIter iter;
|
||
|
||
gflare = list->data;
|
||
|
||
/*
|
||
dlg->gflare should be valid (ie. not NULL) here.
|
||
*/
|
||
gtk_list_store_append (dlg->selector_list, &iter);
|
||
|
||
gtk_list_store_set (dlg->selector_list, &iter,
|
||
0, gflare->name,
|
||
1, gflare,
|
||
-1);
|
||
if (gflare == dlg->gflare)
|
||
gtk_tree_selection_select_iter (dlg->selection,
|
||
&iter);
|
||
|
||
list = list->next;
|
||
n++;
|
||
}
|
||
}
|
||
|
||
static void
|
||
dlg_selector_list_item_callback (GtkTreeSelection *selection)
|
||
{
|
||
GtkTreeIter iter;
|
||
|
||
if (gtk_tree_selection_get_selected (selection, NULL, &iter))
|
||
{
|
||
gtk_tree_model_get (GTK_TREE_MODEL (dlg->selector_list), &iter,
|
||
1, &dlg->gflare, -1);
|
||
}
|
||
|
||
dlg_preview_update ();
|
||
}
|
||
|
||
/*
|
||
* "New" button in Selector page
|
||
*/
|
||
static void
|
||
dlg_selector_new_callback (GtkWidget *widget,
|
||
gpointer data)
|
||
{
|
||
GtkWidget *query_box;
|
||
|
||
query_box = gimp_query_string_box (_("New Gradient Flare"),
|
||
gtk_widget_get_toplevel (widget),
|
||
gimp_standard_help_func, PLUG_IN_PROC,
|
||
_("Enter a name for the new GFlare"),
|
||
_("Unnamed"),
|
||
NULL, NULL,
|
||
dlg_selector_new_ok_callback,
|
||
dlg, NULL);
|
||
gtk_widget_show (query_box);
|
||
}
|
||
|
||
static void
|
||
dlg_selector_new_ok_callback (GtkWidget *widget,
|
||
const gchar *new_name,
|
||
gpointer data)
|
||
{
|
||
GFlare *gflare;
|
||
GtkTreeIter iter;
|
||
gint pos;
|
||
|
||
g_return_if_fail (new_name != NULL);
|
||
|
||
if (gflares_list_lookup (new_name))
|
||
{
|
||
g_message (_("The name '%s' is used already!"), new_name);
|
||
return;
|
||
}
|
||
|
||
gflare = gflare_new_with_default (new_name);
|
||
|
||
pos = gflares_list_insert (gflare);
|
||
|
||
gtk_list_store_insert (dlg->selector_list, &iter, pos);
|
||
gtk_list_store_set (dlg->selector_list, &iter,
|
||
0, gflare->name,
|
||
1, gflare,
|
||
-1);
|
||
gtk_tree_selection_select_iter (dlg->selection, &iter);
|
||
|
||
dlg->gflare = gflare;
|
||
dlg_preview_update ();
|
||
}
|
||
|
||
/*
|
||
* "Edit" button in Selector page
|
||
*/
|
||
static void
|
||
dlg_selector_edit_callback (GtkWidget *widget,
|
||
gpointer data)
|
||
{
|
||
preview_render_end (dlg->preview);
|
||
gtk_widget_set_sensitive (dlg->shell, FALSE);
|
||
ed_run (GTK_WINDOW (dlg->shell),
|
||
dlg->gflare, dlg_selector_edit_done_callback, data);
|
||
}
|
||
|
||
static void
|
||
dlg_selector_edit_done_callback (gint updated,
|
||
gpointer data)
|
||
{
|
||
gtk_widget_set_sensitive (dlg->shell, TRUE);
|
||
if (updated)
|
||
{
|
||
gflare_save (dlg->gflare);
|
||
}
|
||
dlg_preview_update ();
|
||
}
|
||
|
||
/*
|
||
* "Copy" button in Selector page
|
||
*/
|
||
static void
|
||
dlg_selector_copy_callback (GtkWidget *widget,
|
||
gpointer data)
|
||
{
|
||
GtkWidget *query_box;
|
||
gchar *name;
|
||
|
||
name = g_strdup_printf ("%s copy", dlg->gflare->name);
|
||
|
||
query_box = gimp_query_string_box (_("Copy Gradient Flare"),
|
||
gtk_widget_get_toplevel (widget),
|
||
gimp_standard_help_func, PLUG_IN_PROC,
|
||
_("Enter a name for the copied GFlare"),
|
||
name,
|
||
NULL, NULL,
|
||
dlg_selector_copy_ok_callback,
|
||
dlg, NULL);
|
||
g_free (name);
|
||
|
||
gtk_widget_show (query_box);
|
||
}
|
||
|
||
static void
|
||
dlg_selector_copy_ok_callback (GtkWidget *widget,
|
||
const gchar *copy_name,
|
||
gpointer data)
|
||
{
|
||
GFlare *gflare;
|
||
GtkTreeIter iter;
|
||
gint pos;
|
||
|
||
g_return_if_fail (copy_name != NULL);
|
||
|
||
if (gflares_list_lookup (copy_name))
|
||
{
|
||
g_warning (_("The name '%s' is used already!"), copy_name);
|
||
return;
|
||
}
|
||
|
||
gflare = gflare_dup (dlg->gflare, copy_name);
|
||
|
||
pos = gflares_list_insert (gflare);
|
||
gtk_list_store_insert (dlg->selector_list, &iter, pos);
|
||
gtk_list_store_set (dlg->selector_list, &iter,
|
||
0, gflare->name,
|
||
1, gflare,
|
||
-1);
|
||
gtk_tree_selection_select_iter (dlg->selection, &iter);
|
||
|
||
dlg->gflare = gflare;
|
||
gflare_save (dlg->gflare);
|
||
dlg_preview_update ();
|
||
}
|
||
|
||
/*
|
||
* "Delete" button in Selector page
|
||
*/
|
||
static void
|
||
dlg_selector_delete_callback (GtkWidget *widget,
|
||
gpointer data)
|
||
{
|
||
GtkWidget *dialog;
|
||
gchar *str;
|
||
|
||
if (num_gflares <= 1)
|
||
{
|
||
g_message (_("Cannot delete!! There must be at least one GFlare."));
|
||
return;
|
||
}
|
||
|
||
gtk_widget_set_sensitive (dlg->shell, FALSE);
|
||
|
||
str = g_strdup_printf (_("Are you sure you want to delete "
|
||
"\"%s\" from the list and from disk?"),
|
||
dlg->gflare->name);
|
||
|
||
dialog = gimp_query_boolean_box (_("Delete Gradient Flare"),
|
||
dlg->shell,
|
||
gimp_standard_help_func, PLUG_IN_PROC,
|
||
GIMP_ICON_DIALOG_QUESTION,
|
||
str,
|
||
_("_Delete"), _("_Cancel"),
|
||
NULL, NULL,
|
||
dlg_selector_do_delete_callback,
|
||
NULL, NULL);
|
||
|
||
g_free (str);
|
||
|
||
gtk_widget_show (dialog);
|
||
}
|
||
|
||
static void
|
||
dlg_selector_do_delete_callback (GtkWidget *widget,
|
||
gboolean delete,
|
||
gpointer data)
|
||
{
|
||
GFlare *old_gflare;
|
||
GList *tmp;
|
||
gint i, new_i;
|
||
GtkTreeIter iter;
|
||
|
||
gtk_widget_set_sensitive (dlg->shell, TRUE);
|
||
|
||
if (!delete)
|
||
return;
|
||
|
||
i = gflares_list_index (dlg->gflare);
|
||
|
||
if (i >= 0)
|
||
{
|
||
/* Remove current gflare from gflares_list and free it */
|
||
old_gflare = dlg->gflare;
|
||
gflares_list_remove (dlg->gflare);
|
||
dlg->gflare = NULL;
|
||
|
||
/* Remove from listbox */
|
||
if (!gtk_tree_model_iter_nth_child (GTK_TREE_MODEL (dlg->selector_list),
|
||
&iter, NULL, i))
|
||
{
|
||
g_warning ("Unsynchronized lists. Bad things will happen!");
|
||
return;
|
||
}
|
||
gtk_list_store_remove (dlg->selector_list, &iter);
|
||
|
||
/* Calculate new position of gflare and select it */
|
||
new_i = (i < num_gflares) ? i : num_gflares - 1;
|
||
if ((tmp = g_list_nth (gflares_list, new_i)))
|
||
dlg->gflare = tmp->data;
|
||
if (!gtk_tree_model_iter_nth_child (GTK_TREE_MODEL (dlg->selector_list),
|
||
&iter, NULL, i))
|
||
{
|
||
g_warning ("Unsynchronized lists. Bad things will happen!");
|
||
return;
|
||
}
|
||
gtk_tree_selection_select_iter (dlg->selection,
|
||
&iter);
|
||
|
||
/* Delete old one from disk and memory */
|
||
if (old_gflare->filename)
|
||
g_unlink (old_gflare->filename);
|
||
|
||
gflare_free (old_gflare);
|
||
|
||
/* Update */
|
||
dlg_preview_update ();
|
||
}
|
||
else
|
||
{
|
||
g_warning (_("not found %s in gflares_list"), dlg->gflare->name);
|
||
}
|
||
}
|
||
|
||
/*************************************************************************/
|
||
/** **/
|
||
/** GFlare Editor **/
|
||
/** +++ ed **/
|
||
/** **/
|
||
/*************************************************************************/
|
||
|
||
/*
|
||
This is gflare editor dialog, one which opens by clicking
|
||
"Edit" button on the selector page in the main dialog.
|
||
*/
|
||
|
||
static void
|
||
ed_run (GtkWindow *parent,
|
||
GFlare *target_gflare,
|
||
GFlareEditorCallback callback,
|
||
gpointer calldata)
|
||
{
|
||
GtkWidget *shell;
|
||
GtkWidget *hbox;
|
||
GtkWidget *frame;
|
||
GtkWidget *notebook;
|
||
|
||
g_return_if_fail (GIMP_IS_PROCEDURE_CONFIG (calldata));
|
||
|
||
if (!ed)
|
||
ed = g_new0 (GFlareEditor, 1);
|
||
ed->init = TRUE;
|
||
ed->run = FALSE;
|
||
ed->target_gflare = target_gflare;
|
||
ed->gflare = gflare_dup (target_gflare, target_gflare->name);
|
||
ed->callback = callback;
|
||
ed->calldata = calldata;
|
||
|
||
/*
|
||
* Dialog Shell
|
||
*/
|
||
ed->shell =
|
||
shell = gimp_dialog_new (_("Gradient Flare Editor"), PLUG_IN_ROLE,
|
||
GTK_WIDGET (parent), 0,
|
||
gimp_standard_help_func, PLUG_IN_PROC,
|
||
|
||
_("_Rescan Gradients"), RESPONSE_RESCAN,
|
||
_("_Cancel"), GTK_RESPONSE_CANCEL,
|
||
_("_OK"), GTK_RESPONSE_OK,
|
||
|
||
NULL);
|
||
|
||
gimp_dialog_set_alternative_button_order (GTK_DIALOG (shell),
|
||
RESPONSE_RESCAN,
|
||
GTK_RESPONSE_OK,
|
||
GTK_RESPONSE_CANCEL,
|
||
-1);
|
||
|
||
g_signal_connect (shell, "response",
|
||
G_CALLBACK (ed_response),
|
||
ed);
|
||
g_signal_connect (shell, "destroy",
|
||
G_CALLBACK (ed_destroy_callback),
|
||
ed);
|
||
|
||
/*
|
||
* main hbox
|
||
*/
|
||
|
||
hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
|
||
gtk_container_set_border_width (GTK_CONTAINER (hbox), 12);
|
||
gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (shell))),
|
||
hbox, FALSE, FALSE, 0);
|
||
gtk_widget_show (hbox);
|
||
|
||
/*
|
||
* Preview
|
||
*/
|
||
|
||
frame = gtk_frame_new (NULL);
|
||
gtk_widget_set_valign (frame, GTK_ALIGN_START);
|
||
gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
|
||
gtk_box_pack_start (GTK_BOX (hbox), frame, FALSE, FALSE, 0);
|
||
gtk_widget_show (frame);
|
||
|
||
ed->preview = preview_new (ED_PREVIEW_WIDTH, ED_PREVIEW_HEIGHT,
|
||
ed_preview_init_func, calldata,
|
||
ed_preview_render_func, NULL,
|
||
ed_preview_deinit_func, NULL);
|
||
gtk_widget_set_events (GTK_WIDGET (ed->preview->widget), DLG_PREVIEW_MASK);
|
||
gtk_container_add (GTK_CONTAINER (frame), ed->preview->widget);
|
||
g_signal_connect (ed->preview->widget, "event",
|
||
G_CALLBACK (dlg_preview_handle_event),
|
||
calldata);
|
||
ed_preview_calc_window ();
|
||
|
||
/*
|
||
* Notebook
|
||
*/
|
||
notebook = ed->notebook = gtk_notebook_new ();
|
||
gtk_notebook_set_tab_pos (GTK_NOTEBOOK (notebook), GTK_POS_TOP);
|
||
gtk_box_pack_start (GTK_BOX (hbox), notebook, TRUE, TRUE, 0);
|
||
gtk_widget_show (notebook);
|
||
|
||
ed_make_page_general (ed, notebook);
|
||
ed_make_page_glow (ed, notebook);
|
||
ed_make_page_rays (ed, notebook);
|
||
ed_make_page_sflare (ed, notebook);
|
||
|
||
gtk_widget_show (shell);
|
||
|
||
ed->init = FALSE;
|
||
ed_preview_update ();
|
||
}
|
||
|
||
static void
|
||
ed_destroy_callback (GtkWidget *widget,
|
||
GFlareEditor *ed)
|
||
{
|
||
preview_free (ed->preview);
|
||
gflare_free (ed->gflare);
|
||
if (ed->callback)
|
||
(*ed->callback) (ed->run, ed->calldata);
|
||
}
|
||
|
||
static void
|
||
ed_response (GtkWidget *widget,
|
||
gint response_id,
|
||
GFlareEditor *ed)
|
||
{
|
||
switch (response_id)
|
||
{
|
||
case RESPONSE_RESCAN:
|
||
ed->init = TRUE;
|
||
gradient_menu_rescan ();
|
||
ed->init = FALSE;
|
||
ed_preview_update ();
|
||
gtk_widget_queue_draw (ed->notebook);
|
||
break;
|
||
|
||
case GTK_RESPONSE_OK:
|
||
ed->run = TRUE;
|
||
gflare_copy (ed->target_gflare, ed->gflare);
|
||
gtk_widget_destroy (ed->shell);
|
||
break;
|
||
|
||
default:
|
||
gtk_widget_destroy (ed->shell);
|
||
break;
|
||
}
|
||
}
|
||
|
||
static void
|
||
ed_make_page_general (GFlareEditor *ed,
|
||
GtkWidget *notebook)
|
||
{
|
||
GFlare *gflare = ed->gflare;
|
||
GtkWidget *vbox;
|
||
GtkWidget *frame;
|
||
GtkWidget *grid;
|
||
GtkWidget *combo;
|
||
GtkWidget *scale;
|
||
|
||
vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
|
||
gtk_container_set_border_width (GTK_CONTAINER (vbox), 12);
|
||
|
||
/* Glow */
|
||
|
||
frame = gimp_frame_new (_("Glow Paint Options"));
|
||
gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
|
||
gtk_widget_show (frame);
|
||
|
||
grid = gtk_grid_new ();
|
||
gtk_grid_set_row_spacing (GTK_GRID (grid), 6);
|
||
gtk_grid_set_column_spacing (GTK_GRID (grid), 6);
|
||
gtk_container_add (GTK_CONTAINER (frame), grid);
|
||
gtk_widget_show (grid);
|
||
|
||
scale = gimp_scale_entry_new (_("Opacity:"), gflare->glow_opacity, 0.0, 100.0, 1);
|
||
g_signal_connect (scale, "value-changed",
|
||
G_CALLBACK (gradient_scale_entry_update_double),
|
||
&gflare->glow_opacity);
|
||
g_signal_connect (scale, "value-changed",
|
||
G_CALLBACK (ed_preview_update),
|
||
NULL);
|
||
gtk_grid_attach (GTK_GRID (grid), scale, 0, 0, 3, 1);
|
||
gtk_widget_show (scale);
|
||
|
||
combo = ed_mode_menu_new (&gflare->glow_mode);
|
||
gimp_grid_attach_aligned (GTK_GRID (grid), 0, 1,
|
||
_("Paint mode:"), 0.0, 0.5, combo, 1);
|
||
|
||
/* Rays */
|
||
|
||
frame = gimp_frame_new (_("Rays Paint Options"));
|
||
gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
|
||
gtk_widget_show (frame);
|
||
|
||
grid = gtk_grid_new ();
|
||
gtk_grid_set_row_spacing (GTK_GRID (grid), 6);
|
||
gtk_grid_set_column_spacing (GTK_GRID (grid), 6);
|
||
gtk_container_add (GTK_CONTAINER (frame), grid);
|
||
gtk_widget_show (grid);
|
||
|
||
scale = gimp_scale_entry_new (_("Opacity:"), gflare->rays_opacity, 0.0, 100.0, 1);
|
||
g_signal_connect (scale, "value-changed",
|
||
G_CALLBACK (gradient_scale_entry_update_double),
|
||
&gflare->rays_opacity);
|
||
g_signal_connect (scale, "value-changed",
|
||
G_CALLBACK (ed_preview_update),
|
||
NULL);
|
||
gtk_grid_attach (GTK_GRID (grid), scale, 0, 0, 3, 1);
|
||
gtk_widget_show (scale);
|
||
|
||
combo = ed_mode_menu_new (&gflare->rays_mode);
|
||
gimp_grid_attach_aligned (GTK_GRID (grid), 0, 1,
|
||
_("Paint mode:"), 0.0, 0.5, combo, 1);
|
||
|
||
/* Rays */
|
||
|
||
frame = gimp_frame_new (_("Second Flares Paint Options"));
|
||
gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
|
||
gtk_widget_show (frame);
|
||
|
||
grid = gtk_grid_new ();
|
||
gtk_grid_set_row_spacing (GTK_GRID (grid), 6);
|
||
gtk_grid_set_column_spacing (GTK_GRID (grid), 6);
|
||
gtk_container_add (GTK_CONTAINER (frame), grid);
|
||
gtk_widget_show (grid);
|
||
|
||
scale = gimp_scale_entry_new (_("Opacity:"), gflare->sflare_opacity, 0.0, 100.0, 1);
|
||
g_signal_connect (scale, "value-changed",
|
||
G_CALLBACK (gradient_scale_entry_update_double),
|
||
&gflare->sflare_opacity);
|
||
g_signal_connect (scale, "value-changed",
|
||
G_CALLBACK (ed_preview_update),
|
||
NULL);
|
||
gtk_grid_attach (GTK_GRID (grid), scale, 0, 0, 3, 1);
|
||
gtk_widget_show (scale);
|
||
|
||
combo = ed_mode_menu_new (&gflare->sflare_mode);
|
||
gimp_grid_attach_aligned (GTK_GRID (grid), 0, 1,
|
||
_("Paint mode:"), 0.0, 0.5, combo, 1);
|
||
|
||
gtk_notebook_append_page (GTK_NOTEBOOK (notebook), vbox,
|
||
gtk_label_new_with_mnemonic (_("_General")));
|
||
g_signal_connect (vbox, "map",
|
||
G_CALLBACK (ed_page_map_callback),
|
||
(gpointer) PAGE_GENERAL);
|
||
gtk_widget_show (vbox);
|
||
}
|
||
|
||
static void
|
||
ed_make_page_glow (GFlareEditor *ed,
|
||
GtkWidget *notebook)
|
||
{
|
||
GFlare *gflare = ed->gflare;
|
||
GradientMenu *gm;
|
||
GtkWidget *vbox;
|
||
GtkWidget *frame;
|
||
GtkWidget *grid;
|
||
GtkWidget *scale;
|
||
gint row;
|
||
|
||
vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
|
||
gtk_container_set_border_width (GTK_CONTAINER (vbox), 12);
|
||
|
||
/*
|
||
* Gradient Menus
|
||
*/
|
||
|
||
frame = gimp_frame_new (_("Gradients"));
|
||
gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
|
||
gtk_widget_show (frame);
|
||
|
||
grid = gtk_grid_new ();
|
||
gtk_grid_set_row_spacing (GTK_GRID (grid), 6);
|
||
gtk_grid_set_column_spacing (GTK_GRID (grid), 6);
|
||
gtk_container_add (GTK_CONTAINER (frame), grid);
|
||
|
||
gm = gradient_menu_new ((GradientMenuCallback) &ed_gradient_menu_callback,
|
||
gflare->glow_radial, gflare->glow_radial);
|
||
ed_put_gradient_menu (grid, 0, 0, _("Radial gradient:"), gm);
|
||
|
||
gm = gradient_menu_new ((GradientMenuCallback) &ed_gradient_menu_callback,
|
||
gflare->glow_angular, gflare->glow_angular);
|
||
ed_put_gradient_menu (grid, 0, 1, _("Angular gradient:"), gm);
|
||
|
||
gm = gradient_menu_new ((GradientMenuCallback) &ed_gradient_menu_callback,
|
||
gflare->glow_angular_size, gflare->glow_angular_size);
|
||
ed_put_gradient_menu (grid, 0, 2, _("Angular size gradient:"), gm);
|
||
|
||
gtk_widget_show (grid);
|
||
|
||
/*
|
||
* Scales
|
||
*/
|
||
|
||
frame = gimp_frame_new (_("Parameters"));
|
||
gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
|
||
gtk_widget_show (frame);
|
||
|
||
grid = gtk_grid_new ();
|
||
gtk_grid_set_row_spacing (GTK_GRID (grid), 6);
|
||
gtk_grid_set_column_spacing (GTK_GRID (grid), 6);
|
||
gtk_container_add (GTK_CONTAINER (frame), grid);
|
||
|
||
row = 0;
|
||
|
||
scale = gimp_scale_entry_new (_("Size (%):"), gflare->glow_size, 0.0, G_MAXINT, 1);
|
||
gimp_scale_entry_set_bounds (GIMP_SCALE_ENTRY (scale), 0.0, 200.0, TRUE);
|
||
g_signal_connect (scale, "value-changed",
|
||
G_CALLBACK (gradient_scale_entry_update_double),
|
||
&gflare->glow_size);
|
||
g_signal_connect (scale, "value-changed",
|
||
G_CALLBACK (ed_preview_update),
|
||
NULL);
|
||
gtk_grid_attach (GTK_GRID (grid), scale, 0, row++, 3, 1);
|
||
gtk_widget_show (scale);
|
||
|
||
scale = gimp_scale_entry_new (_("Rotation:"), gflare->glow_rotation, -180.0, 180.0, 1);
|
||
gimp_label_spin_set_increments (GIMP_LABEL_SPIN (scale), 1.0, 15.0);
|
||
g_signal_connect (scale, "value-changed",
|
||
G_CALLBACK (gradient_scale_entry_update_double),
|
||
&gflare->glow_rotation);
|
||
g_signal_connect (scale, "value-changed",
|
||
G_CALLBACK (ed_preview_update),
|
||
NULL);
|
||
gtk_grid_attach (GTK_GRID (grid), scale, 0, row++, 3, 1);
|
||
gtk_widget_show (scale);
|
||
|
||
scale = gimp_scale_entry_new (_("Hue rotation:"), gflare->glow_hue, -180.0, 180.0, 1);
|
||
gimp_label_spin_set_increments (GIMP_LABEL_SPIN (scale), 1.0, 15.0);
|
||
g_signal_connect (scale, "value-changed",
|
||
G_CALLBACK (gradient_scale_entry_update_double),
|
||
&gflare->glow_hue);
|
||
g_signal_connect (scale, "value-changed",
|
||
G_CALLBACK (ed_preview_update),
|
||
NULL);
|
||
gtk_grid_attach (GTK_GRID (grid), scale, 0, row++, 3, 1);
|
||
gtk_widget_show (scale);
|
||
|
||
gtk_widget_show (grid);
|
||
|
||
gtk_notebook_append_page (GTK_NOTEBOOK (notebook), vbox,
|
||
gtk_label_new_with_mnemonic (_("G_low")));
|
||
g_signal_connect (vbox, "map",
|
||
G_CALLBACK (ed_page_map_callback),
|
||
(gpointer) PAGE_GLOW);
|
||
gtk_widget_show (vbox);
|
||
}
|
||
|
||
static void
|
||
ed_make_page_rays (GFlareEditor *ed,
|
||
GtkWidget *notebook)
|
||
{
|
||
GFlare *gflare = ed->gflare;
|
||
GradientMenu *gm;
|
||
GtkWidget *vbox;
|
||
GtkWidget *frame;
|
||
GtkWidget *grid;
|
||
GtkWidget *scale;
|
||
gint row;
|
||
|
||
vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
|
||
gtk_container_set_border_width (GTK_CONTAINER (vbox), 12);
|
||
|
||
/*
|
||
* Gradient Menus
|
||
*/
|
||
|
||
frame = gimp_frame_new (_("Gradients"));
|
||
gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
|
||
gtk_widget_show (frame);
|
||
|
||
grid = gtk_grid_new ();
|
||
gtk_grid_set_row_spacing (GTK_GRID (grid), 6);
|
||
gtk_grid_set_column_spacing (GTK_GRID (grid), 6);
|
||
gtk_container_add (GTK_CONTAINER (frame), grid);
|
||
|
||
row = 0;
|
||
|
||
gm = gradient_menu_new ((GradientMenuCallback) &ed_gradient_menu_callback,
|
||
gflare->rays_radial, gflare->rays_radial);
|
||
ed_put_gradient_menu (grid, 0, row++, _("Radial gradient:"), gm);
|
||
|
||
gm = gradient_menu_new ((GradientMenuCallback) &ed_gradient_menu_callback,
|
||
gflare->rays_angular, gflare->rays_angular);
|
||
ed_put_gradient_menu (grid, 0, row++, _("Angular gradient:"), gm);
|
||
|
||
gm = gradient_menu_new ((GradientMenuCallback) &ed_gradient_menu_callback,
|
||
gflare->rays_angular_size, gflare->rays_angular_size);
|
||
ed_put_gradient_menu (grid, 0, row++, _("Angular size gradient:"), gm);
|
||
|
||
gtk_widget_show (grid);
|
||
|
||
/*
|
||
* Scales
|
||
*/
|
||
|
||
frame = gimp_frame_new (_("Parameters"));
|
||
gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
|
||
gtk_widget_show (frame);
|
||
|
||
grid = gtk_grid_new ();
|
||
gtk_grid_set_row_spacing (GTK_GRID (grid), 6);
|
||
gtk_grid_set_column_spacing (GTK_GRID (grid), 6);
|
||
gtk_container_add (GTK_CONTAINER (frame), grid);
|
||
|
||
row = 0;
|
||
|
||
scale = gimp_scale_entry_new (_("Size (%):"), gflare->rays_size, 0.0, G_MAXINT, 1);
|
||
gimp_scale_entry_set_bounds (GIMP_SCALE_ENTRY (scale), 0.0, 200.0, TRUE);
|
||
g_signal_connect (scale, "value-changed",
|
||
G_CALLBACK (gradient_scale_entry_update_double),
|
||
&gflare->rays_size);
|
||
g_signal_connect (scale, "value-changed",
|
||
G_CALLBACK (ed_preview_update),
|
||
NULL);
|
||
gtk_grid_attach (GTK_GRID (grid), scale, 0, row++, 3, 1);
|
||
gtk_widget_show (scale);
|
||
|
||
scale = gimp_scale_entry_new (_("Rotation:"), gflare->rays_rotation, -180.0, 180.0, 1);
|
||
gimp_label_spin_set_increments (GIMP_LABEL_SPIN (scale), 1.0, 15.0);
|
||
g_signal_connect (scale, "value-changed",
|
||
G_CALLBACK (gradient_scale_entry_update_double),
|
||
&gflare->rays_rotation);
|
||
g_signal_connect (scale, "value-changed",
|
||
G_CALLBACK (ed_preview_update),
|
||
NULL);
|
||
gtk_grid_attach (GTK_GRID (grid), scale, 0, row++, 3, 1);
|
||
gtk_widget_show (scale);
|
||
|
||
scale = gimp_scale_entry_new (_("Hue rotation:"), gflare->rays_hue, -180.0, 180.0, 1);
|
||
gimp_label_spin_set_increments (GIMP_LABEL_SPIN (scale), 1.0, 15.0);
|
||
g_signal_connect (scale, "value-changed",
|
||
G_CALLBACK (gradient_scale_entry_update_double),
|
||
&gflare->rays_hue);
|
||
g_signal_connect (scale, "value-changed",
|
||
G_CALLBACK (ed_preview_update),
|
||
NULL);
|
||
gtk_grid_attach (GTK_GRID (grid), scale, 0, row++, 3, 1);
|
||
gtk_widget_show (scale);
|
||
|
||
scale = gimp_scale_entry_new (_("# of Spikes:"), gflare->rays_nspikes, 1, G_MAXINT, 0);
|
||
gimp_scale_entry_set_bounds (GIMP_SCALE_ENTRY (scale), 1.0, 300.0, TRUE);
|
||
g_signal_connect (scale, "value-changed",
|
||
G_CALLBACK (gradient_scale_entry_update_int),
|
||
&gflare->rays_nspikes);
|
||
g_signal_connect (scale, "value-changed",
|
||
G_CALLBACK (ed_preview_update),
|
||
NULL);
|
||
gtk_grid_attach (GTK_GRID (grid), scale, 0, row++, 3, 1);
|
||
gtk_widget_show (scale);
|
||
|
||
scale = gimp_scale_entry_new (_("Spike thickness:"), gflare->rays_thickness, 1.0, GIMP_MAX_IMAGE_SIZE, 1);
|
||
gimp_scale_entry_set_bounds (GIMP_SCALE_ENTRY (scale), 1.0, 100.0, TRUE);
|
||
g_signal_connect (scale, "value-changed",
|
||
G_CALLBACK (gradient_scale_entry_update_double),
|
||
&gflare->rays_thickness);
|
||
g_signal_connect (scale, "value-changed",
|
||
G_CALLBACK (ed_preview_update),
|
||
NULL);
|
||
gtk_grid_attach (GTK_GRID (grid), scale, 0, row++, 3, 1);
|
||
gtk_widget_show (scale);
|
||
|
||
gtk_widget_show (grid);
|
||
|
||
gtk_notebook_append_page (GTK_NOTEBOOK (notebook), vbox,
|
||
gtk_label_new_with_mnemonic (_("_Rays")));
|
||
g_signal_connect (vbox, "map",
|
||
G_CALLBACK (ed_page_map_callback),
|
||
(gpointer) PAGE_RAYS);
|
||
gtk_widget_show (vbox);
|
||
}
|
||
|
||
static void
|
||
ed_make_page_sflare (GFlareEditor *ed,
|
||
GtkWidget *notebook)
|
||
{
|
||
GFlare *gflare = ed->gflare;
|
||
GradientMenu *gm;
|
||
GtkWidget *vbox;
|
||
GtkWidget *grid;
|
||
GtkWidget *frame;
|
||
GtkWidget *shape_vbox;
|
||
GSList *shape_group = NULL;
|
||
GtkWidget *polygon_hbox;
|
||
GtkWidget *seed_hbox;
|
||
GtkWidget *toggle;
|
||
GtkWidget *label;
|
||
GtkWidget *seed;
|
||
GtkWidget *entry;
|
||
GtkWidget *scale;
|
||
gchar buf[256];
|
||
gint row;
|
||
|
||
vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
|
||
gtk_container_set_border_width (GTK_CONTAINER (vbox), 12);
|
||
|
||
/*
|
||
* Gradient Menus
|
||
*/
|
||
|
||
frame = gimp_frame_new (_("Gradients"));
|
||
gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
|
||
gtk_widget_show (frame);
|
||
|
||
grid = gtk_grid_new ();
|
||
gtk_grid_set_row_spacing (GTK_GRID (grid), 6);
|
||
gtk_grid_set_column_spacing (GTK_GRID (grid), 6);
|
||
gtk_container_add (GTK_CONTAINER (frame), grid);
|
||
|
||
gm = gradient_menu_new ((GradientMenuCallback) &ed_gradient_menu_callback,
|
||
gflare->sflare_radial, gflare->sflare_radial);
|
||
ed_put_gradient_menu (grid, 0, 0, _("Radial gradient:"), gm);
|
||
|
||
gm = gradient_menu_new ((GradientMenuCallback) &ed_gradient_menu_callback,
|
||
gflare->sflare_sizefac, gflare->sflare_sizefac);
|
||
ed_put_gradient_menu (grid, 0, 1, _("Size factor gradient:"), gm);
|
||
|
||
gm = gradient_menu_new ((GradientMenuCallback) &ed_gradient_menu_callback,
|
||
gflare->sflare_probability, gflare->sflare_probability);
|
||
ed_put_gradient_menu (grid, 0, 2, _("Probability gradient:"), gm);
|
||
|
||
gtk_widget_show (grid);
|
||
|
||
/*
|
||
* Scales
|
||
*/
|
||
|
||
frame = gimp_frame_new (_("Parameters"));
|
||
gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
|
||
gtk_widget_show (frame);
|
||
|
||
grid = gtk_grid_new ();
|
||
gtk_grid_set_row_spacing (GTK_GRID (grid), 6);
|
||
gtk_grid_set_column_spacing (GTK_GRID (grid), 6);
|
||
gtk_container_add (GTK_CONTAINER (frame), grid);
|
||
|
||
row = 0;
|
||
|
||
scale = gimp_scale_entry_new (_("Size (%):"), gflare->sflare_size, 0.0, G_MAXINT, 1);
|
||
gimp_scale_entry_set_bounds (GIMP_SCALE_ENTRY (scale), 1.0, 200.0, TRUE);
|
||
g_signal_connect (scale, "value-changed",
|
||
G_CALLBACK (gradient_scale_entry_update_double),
|
||
&gflare->sflare_size);
|
||
g_signal_connect (scale, "value-changed",
|
||
G_CALLBACK (ed_preview_update),
|
||
NULL);
|
||
gtk_grid_attach (GTK_GRID (grid), scale, 0, row++, 3, 1);
|
||
gtk_widget_show (scale);
|
||
|
||
scale = gimp_scale_entry_new (_("Rotation:"), gflare->sflare_rotation, -180.0, 180.0, 1);
|
||
gimp_label_spin_set_increments (GIMP_LABEL_SPIN (scale), 1.0, 15.0);
|
||
g_signal_connect (scale, "value-changed",
|
||
G_CALLBACK (gradient_scale_entry_update_double),
|
||
&gflare->sflare_rotation);
|
||
g_signal_connect (scale, "value-changed",
|
||
G_CALLBACK (ed_preview_update),
|
||
NULL);
|
||
gtk_grid_attach (GTK_GRID (grid), scale, 0, row++, 3, 1);
|
||
gtk_widget_show (scale);
|
||
|
||
scale = gimp_scale_entry_new (_("Hue rotation:"), gflare->sflare_hue, -180.0, 180.0, 1);
|
||
gimp_label_spin_set_increments (GIMP_LABEL_SPIN (scale), 1.0, 15.0);
|
||
g_signal_connect (scale, "value-changed",
|
||
G_CALLBACK (gradient_scale_entry_update_double),
|
||
&gflare->sflare_hue);
|
||
g_signal_connect (scale, "value-changed",
|
||
G_CALLBACK (ed_preview_update),
|
||
NULL);
|
||
gtk_grid_attach (GTK_GRID (grid), scale, 0, row++, 3, 1);
|
||
gtk_widget_show (scale);
|
||
|
||
gtk_widget_show (grid);
|
||
|
||
/*
|
||
* Shape Radio Button Frame
|
||
*/
|
||
|
||
frame = gimp_frame_new (_("Shape of Second Flares"));
|
||
gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
|
||
gtk_widget_show (frame);
|
||
|
||
shape_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 2);
|
||
gtk_container_add (GTK_CONTAINER (frame), shape_vbox);
|
||
gtk_widget_show (shape_vbox);
|
||
|
||
toggle = gtk_radio_button_new_with_label (shape_group, _("Circle"));
|
||
shape_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (toggle));
|
||
g_object_set_data (G_OBJECT (toggle), "gimp-item-data",
|
||
GINT_TO_POINTER (GF_CIRCLE));
|
||
g_signal_connect (toggle, "toggled",
|
||
G_CALLBACK (ed_shape_radio_callback),
|
||
&gflare->sflare_shape);
|
||
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle),
|
||
gflare->sflare_shape == GF_CIRCLE);
|
||
gtk_box_pack_start (GTK_BOX (shape_vbox), toggle, FALSE, FALSE, 0);
|
||
gtk_widget_show (toggle);
|
||
|
||
polygon_hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
|
||
gtk_box_pack_start (GTK_BOX (shape_vbox), polygon_hbox, FALSE, FALSE, 0);
|
||
gtk_widget_show (polygon_hbox);
|
||
|
||
toggle = ed->polygon_toggle =
|
||
gtk_radio_button_new_with_label (shape_group, _("Polygon"));
|
||
shape_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (toggle));
|
||
g_object_set_data (G_OBJECT (toggle), "gimp-item-data",
|
||
GINT_TO_POINTER (GF_POLYGON));
|
||
g_signal_connect (toggle, "toggled",
|
||
G_CALLBACK (ed_shape_radio_callback),
|
||
&gflare->sflare_shape);
|
||
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle),
|
||
gflare->sflare_shape == GF_POLYGON);
|
||
gtk_box_pack_start (GTK_BOX (polygon_hbox), toggle, FALSE, FALSE, 0);
|
||
gtk_widget_show (toggle);
|
||
|
||
entry = ed->polygon_entry = gtk_entry_new ();
|
||
gtk_entry_set_width_chars (GTK_ENTRY (entry), 4);
|
||
g_snprintf (buf, sizeof (buf), "%d", gflare->sflare_nverts);
|
||
gtk_entry_set_text (GTK_ENTRY (entry), buf);
|
||
g_signal_connect (entry, "changed",
|
||
G_CALLBACK (ed_ientry_callback),
|
||
&gflare->sflare_nverts);
|
||
gtk_box_pack_start (GTK_BOX (polygon_hbox), entry, FALSE, FALSE, 0);
|
||
gtk_widget_show (entry);
|
||
|
||
g_object_bind_property (toggle, "active",
|
||
entry, "sensitive",
|
||
G_BINDING_SYNC_CREATE);
|
||
|
||
/*
|
||
* Random Seed Entry
|
||
*/
|
||
|
||
seed_hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
|
||
gtk_box_pack_start (GTK_BOX (vbox), seed_hbox, FALSE, FALSE, 0);
|
||
gtk_widget_show (seed_hbox);
|
||
|
||
label = gtk_label_new (_("Random seed:"));
|
||
gtk_label_set_xalign (GTK_LABEL (label), 0.0);
|
||
gtk_box_pack_start (GTK_BOX (seed_hbox), label, FALSE, FALSE, 0);
|
||
gtk_widget_show (label);
|
||
|
||
seed = gimp_random_seed_new (&gflare->sflare_seed, &gflare->random_seed);
|
||
gtk_box_pack_start (GTK_BOX (seed_hbox), seed, FALSE, TRUE, 0);
|
||
gtk_widget_show (seed);
|
||
|
||
g_signal_connect (GIMP_RANDOM_SEED_SPINBUTTON_ADJ (seed), "value-changed",
|
||
G_CALLBACK (ed_preview_update),
|
||
NULL);
|
||
|
||
gtk_notebook_append_page (GTK_NOTEBOOK (notebook), vbox,
|
||
gtk_label_new_with_mnemonic (_("_Second Flares")));
|
||
g_signal_connect (vbox, "map",
|
||
G_CALLBACK (ed_page_map_callback),
|
||
(gpointer) PAGE_SFLARE);
|
||
gtk_widget_show (vbox);
|
||
}
|
||
|
||
GtkWidget *
|
||
ed_mode_menu_new (GFlareMode *mode_var)
|
||
{
|
||
GtkWidget *combo = gimp_int_combo_box_new_array (GF_NUM_MODES,
|
||
gflare_menu_modes);
|
||
|
||
gimp_int_combo_box_connect (GIMP_INT_COMBO_BOX (combo), *mode_var,
|
||
G_CALLBACK (ed_mode_menu_callback),
|
||
mode_var, NULL);
|
||
|
||
return combo;
|
||
}
|
||
|
||
/*
|
||
puts gradient menu with caption into grid
|
||
occupies 1 row and 3 cols in grid
|
||
*/
|
||
static void
|
||
ed_put_gradient_menu (GtkWidget *grid,
|
||
gint x,
|
||
gint y,
|
||
const gchar *caption,
|
||
GradientMenu *gm)
|
||
{
|
||
GtkWidget *label;
|
||
|
||
label = gtk_label_new (caption);
|
||
gtk_label_set_xalign (GTK_LABEL (label), 1.0);
|
||
gtk_widget_show (label);
|
||
|
||
gtk_grid_attach (GTK_GRID (grid), label, x, y, 1, 1);
|
||
gtk_grid_attach (GTK_GRID (grid), gm->preview, x + 1, y, 1, 1);
|
||
gtk_grid_attach (GTK_GRID (grid), gm->combo, x + 2, y, 1, 1);
|
||
}
|
||
|
||
static void
|
||
ed_mode_menu_callback (GtkWidget *widget,
|
||
gpointer data)
|
||
{
|
||
gimp_int_combo_box_get_active (GIMP_INT_COMBO_BOX (widget), (gint *) data);
|
||
|
||
ed_preview_update ();
|
||
}
|
||
|
||
static void
|
||
ed_gradient_menu_callback (const gchar *gradient_name,
|
||
gpointer data)
|
||
{
|
||
gchar *dest_string = data;
|
||
|
||
/* @GRADIENT_NAME */
|
||
gradient_name_copy (dest_string, gradient_name);
|
||
ed_preview_update ();
|
||
}
|
||
|
||
static void
|
||
ed_shape_radio_callback (GtkWidget *widget,
|
||
gpointer data)
|
||
{
|
||
gimp_radio_button_update (widget, data);
|
||
|
||
ed_preview_update ();
|
||
}
|
||
|
||
static void
|
||
ed_ientry_callback (GtkWidget *widget,
|
||
gpointer data)
|
||
{
|
||
gint new_val;
|
||
|
||
new_val = atoi (gtk_entry_get_text (GTK_ENTRY (widget)));
|
||
*(gint *)data = new_val;
|
||
|
||
ed_preview_update ();
|
||
}
|
||
|
||
/*
|
||
NOTE: This is hack, because this code depends on internal "map"
|
||
signal of changing pages of gtknotebook.
|
||
*/
|
||
static void
|
||
ed_page_map_callback (GtkWidget *widget,
|
||
gpointer data)
|
||
{
|
||
gint page_num = GPOINTER_TO_INT (data);
|
||
|
||
DEBUG_PRINT(("ed_page_map_callback\n"));
|
||
|
||
ed->cur_page = page_num;
|
||
ed_preview_update ();
|
||
}
|
||
|
||
|
||
static void
|
||
ed_preview_update (void)
|
||
{
|
||
if (ed->init)
|
||
return;
|
||
|
||
ed->init_params_done = FALSE;
|
||
preview_render_start (ed->preview);
|
||
}
|
||
|
||
static gint
|
||
ed_preview_init_func (Preview *preview, gpointer data)
|
||
{
|
||
GimpProcedureConfig *config = GIMP_PROCEDURE_CONFIG (data);
|
||
int type = 0;
|
||
|
||
if (ed->init_params_done == FALSE)
|
||
{
|
||
gdouble vangle;
|
||
gdouble vlength;
|
||
|
||
g_object_get (config,
|
||
"vector-angle", &vangle,
|
||
"vector-length", &vlength,
|
||
NULL);
|
||
switch (ed->cur_page)
|
||
{
|
||
case PAGE_GENERAL:
|
||
type = (CALC_GLOW | CALC_RAYS | CALC_SFLARE);
|
||
break;
|
||
case PAGE_GLOW:
|
||
type = CALC_GLOW;
|
||
break;
|
||
case PAGE_RAYS:
|
||
type = CALC_RAYS;
|
||
break;
|
||
case PAGE_SFLARE:
|
||
type = CALC_SFLARE;
|
||
break;
|
||
default:
|
||
g_warning ("ed_preview_edit_func: bad page");
|
||
break;
|
||
}
|
||
calc_init_params (ed->gflare, type,
|
||
ED_PREVIEW_WIDTH/2, ED_PREVIEW_HEIGHT/2,
|
||
ED_PREVIEW_WIDTH/2, 0.0, 0.0,
|
||
vangle, vlength);
|
||
|
||
ed->init_params_done = TRUE;
|
||
return TRUE;
|
||
}
|
||
return calc_init_progress ();
|
||
}
|
||
|
||
static void
|
||
ed_preview_deinit_func (Preview *preview, gpointer data)
|
||
{
|
||
if (ed->init_params_done)
|
||
{
|
||
calc_deinit ();
|
||
ed->init_params_done = FALSE;
|
||
}
|
||
}
|
||
|
||
static void
|
||
ed_preview_render_func (Preview *preview,
|
||
guchar *buffer,
|
||
gint y,
|
||
gpointer data)
|
||
{
|
||
switch (ed->cur_page)
|
||
{
|
||
case PAGE_GENERAL:
|
||
ed_preview_render_general (buffer, y);
|
||
break;
|
||
case PAGE_GLOW:
|
||
ed_preview_render_glow (buffer, y);
|
||
break;
|
||
case PAGE_RAYS:
|
||
ed_preview_render_rays (buffer, y);
|
||
break;
|
||
case PAGE_SFLARE:
|
||
ed_preview_render_sflare (buffer, y);
|
||
break;
|
||
default:
|
||
g_warning ("hmm, bad page in ed_preview_render_func ()");
|
||
break;
|
||
}
|
||
}
|
||
|
||
static void
|
||
ed_preview_render_general (guchar *buffer, gint y)
|
||
{
|
||
int x, i;
|
||
guchar gflare_pix[4];
|
||
static guchar src_pix[4] = {0, 0, 0, OPAQUE};
|
||
int gflare_a;
|
||
|
||
for (x = 0; x < ED_PREVIEW_WIDTH; x++)
|
||
{
|
||
calc_gflare_pix (gflare_pix, x, y, src_pix);
|
||
gflare_a = gflare_pix[3];
|
||
|
||
for (i = 0; i < 3; i++)
|
||
{
|
||
*buffer++ = gflare_pix[i] * gflare_a / 255;
|
||
}
|
||
}
|
||
}
|
||
|
||
static void
|
||
ed_preview_render_glow (guchar *buffer, gint y)
|
||
{
|
||
int x, i;
|
||
guchar pix[4];
|
||
|
||
for (x = 0; x < ED_PREVIEW_WIDTH; x++)
|
||
{
|
||
calc_glow_pix (pix, x, y);
|
||
for (i = 0; i < 3; i++)
|
||
*buffer++ = pix[i] * pix[3] / 255;
|
||
}
|
||
}
|
||
|
||
static void
|
||
ed_preview_render_rays (guchar *buffer, gint y)
|
||
{
|
||
int x, i;
|
||
guchar pix[4];
|
||
|
||
for (x = 0; x < ED_PREVIEW_WIDTH; x++)
|
||
{
|
||
calc_rays_pix (pix, x, y);
|
||
for (i = 0; i < 3; i++)
|
||
*buffer++ = pix[i] * pix[3] / 255;
|
||
}
|
||
}
|
||
|
||
static void
|
||
ed_preview_render_sflare (guchar *buffer, gint y)
|
||
{
|
||
int x, i;
|
||
guchar pix[4];
|
||
static guchar src_pix[4] = {0, 0, 0, OPAQUE};
|
||
|
||
for (x = 0; x < ED_PREVIEW_WIDTH; x++)
|
||
{
|
||
calc_sflare_pix (pix, x, y, src_pix);
|
||
for (i = 0; i < 3; i++)
|
||
*buffer++ = pix[i] * pix[3] / 255;
|
||
}
|
||
}
|
||
|
||
/*************************************************************************/
|
||
/** **/
|
||
/** +++ Preview **/
|
||
/** **/
|
||
/*************************************************************************/
|
||
|
||
/*
|
||
this is generic preview routines.
|
||
*/
|
||
|
||
|
||
/*
|
||
Routines to render the preview in background
|
||
*/
|
||
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)
|
||
{
|
||
Preview *preview;
|
||
|
||
preview = g_new0 (Preview, 1);
|
||
|
||
preview->widget = gimp_preview_area_new ();
|
||
gtk_widget_set_size_request (preview->widget, width, height);
|
||
gtk_widget_show (preview->widget);
|
||
|
||
preview->width = width;
|
||
preview->height = height;
|
||
preview->init_func = init_func;
|
||
preview->init_data = init_data;
|
||
preview->render_func = render_func;
|
||
preview->render_data = render_data;
|
||
preview->deinit_func = deinit_func;
|
||
preview->deinit_data = deinit_data;
|
||
preview->idle_tag = 0;
|
||
preview->buffer = g_new (guchar, width * 3);
|
||
preview->full_image_buffer = g_new (guchar, width * height * 3);
|
||
|
||
return preview;
|
||
}
|
||
|
||
static void
|
||
preview_free (Preview *preview)
|
||
{
|
||
preview_render_end (preview);
|
||
/* not destroy preview->widget */
|
||
g_free (preview->buffer);
|
||
g_free (preview->full_image_buffer);
|
||
g_free (preview);
|
||
}
|
||
|
||
/*
|
||
Start rendering of the preview in background using an idle event.
|
||
If already started and not yet finished, stop it first.
|
||
*/
|
||
static void
|
||
preview_render_start (Preview *preview)
|
||
{
|
||
preview_render_end (preview);
|
||
|
||
preview->init_done = FALSE;
|
||
preview->current_y = 0;
|
||
preview->drawn_y = 0;
|
||
preview->timeout_tag = g_timeout_add (100,
|
||
(GSourceFunc) preview_render_start_2,
|
||
preview);
|
||
}
|
||
|
||
static gint
|
||
preview_render_start_2 (Preview *preview)
|
||
{
|
||
preview->timeout_tag = 0;
|
||
preview->idle_tag = g_idle_add ((GSourceFunc) preview_handle_idle, preview);
|
||
return FALSE;
|
||
}
|
||
|
||
static void
|
||
preview_render_end (Preview *preview)
|
||
{
|
||
if (preview->timeout_tag > 0)
|
||
{
|
||
g_source_remove (preview->timeout_tag);
|
||
preview->timeout_tag = 0;
|
||
}
|
||
if (preview->idle_tag > 0)
|
||
{
|
||
if (preview->deinit_func)
|
||
(*preview->deinit_func) (preview, preview->deinit_data);
|
||
|
||
g_source_remove (preview->idle_tag);
|
||
preview->idle_tag = 0;
|
||
}
|
||
}
|
||
|
||
/*
|
||
Handle an idle event.
|
||
Return FALSE if done, TRUE otherwise.
|
||
*/
|
||
static gboolean
|
||
preview_handle_idle (Preview *preview)
|
||
{
|
||
gboolean done = FALSE;
|
||
|
||
if (preview->init_done == FALSE)
|
||
{
|
||
if (preview->init_func &&
|
||
(*preview->init_func) (preview, preview->init_data))
|
||
return TRUE;
|
||
|
||
preview->init_done = TRUE;
|
||
}
|
||
|
||
if (preview->render_func)
|
||
(*preview->render_func) (preview, preview->buffer, preview->current_y,
|
||
preview->render_data);
|
||
else
|
||
memset (preview->buffer, 0, preview->width * 3);
|
||
|
||
memcpy (preview->full_image_buffer + preview->width * 3 * preview->current_y,
|
||
preview->buffer,
|
||
preview->width * 3);
|
||
|
||
if (++preview->current_y >= preview->height)
|
||
done = TRUE;
|
||
|
||
if (done)
|
||
{
|
||
gimp_preview_area_draw (GIMP_PREVIEW_AREA (preview->widget),
|
||
0, 0, preview->width, preview->height,
|
||
GIMP_RGB_IMAGE,
|
||
preview->full_image_buffer,
|
||
preview->width * 3);
|
||
|
||
preview->drawn_y = preview->current_y;
|
||
preview_render_end (preview);
|
||
return FALSE;
|
||
}
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
/*
|
||
Convert RGBA to RGB with rendering gray check if needed.
|
||
(from nova.c)
|
||
input: guchar src[4] RGBA pixel
|
||
output: guchar dest[3] RGB pixel
|
||
*/
|
||
|
||
static void
|
||
preview_rgba_to_rgb (guchar *dest,
|
||
gint x,
|
||
gint y,
|
||
guchar *src)
|
||
{
|
||
gint src_a;
|
||
gint check;
|
||
gint b;
|
||
|
||
src_a = src[3];
|
||
|
||
if (src_a == OPAQUE) /* full opaque */
|
||
{
|
||
for (b = 0; b < 3; b++)
|
||
dest[b] = src[b];
|
||
}
|
||
else
|
||
{
|
||
if ((x % (GIMP_CHECK_SIZE) < GIMP_CHECK_SIZE_SM) ^
|
||
(y % (GIMP_CHECK_SIZE) < GIMP_CHECK_SIZE_SM))
|
||
check = GIMP_CHECK_LIGHT * 255;
|
||
else
|
||
check = GIMP_CHECK_DARK * 255;
|
||
|
||
if (src_a == 0) /* full transparent */
|
||
{
|
||
for (b = 0; b < 3; b++)
|
||
dest[b] = check;
|
||
}
|
||
else
|
||
{
|
||
for (b = 0; b < 3; b++)
|
||
dest[b] = (src[b] * src_a + check * (OPAQUE-src_a)) / OPAQUE;
|
||
}
|
||
}
|
||
}
|
||
|
||
/*************************************************************************/
|
||
/** **/
|
||
/** +++ Gradient Menu **/
|
||
/** +++ gm **/
|
||
/** **/
|
||
/*************************************************************************/
|
||
|
||
static void
|
||
gradient_menu_init (void)
|
||
{
|
||
gm_gradient_get_list ();
|
||
gradient_menus = NULL;
|
||
}
|
||
|
||
static void
|
||
gradient_menu_rescan (void)
|
||
{
|
||
GList *tmp;
|
||
GradientMenu *gm;
|
||
GtkTreeModel *model;
|
||
|
||
/* Detach and destroy menus first */
|
||
tmp = gradient_menus;
|
||
while (tmp)
|
||
{
|
||
gm = tmp->data;
|
||
tmp = tmp->next;
|
||
|
||
model = gtk_combo_box_get_model (GTK_COMBO_BOX (gm->combo));
|
||
gtk_list_store_clear (GTK_LIST_STORE (model));
|
||
}
|
||
|
||
gm_gradient_get_list ();
|
||
|
||
tmp = gradient_menus;
|
||
while (tmp)
|
||
{
|
||
gm = tmp->data;
|
||
tmp = tmp->next;
|
||
|
||
gm_gradient_combo_fill (gm, gm->gradient_name);
|
||
}
|
||
}
|
||
|
||
static GradientMenu *
|
||
gradient_menu_new (GradientMenuCallback callback,
|
||
gpointer callback_data,
|
||
const gchar *default_gradient_name)
|
||
{
|
||
GradientMenu *gm = g_new (GradientMenu, 1);
|
||
|
||
gm->callback = callback;
|
||
gm->callback_data = callback_data;
|
||
|
||
gm->preview = gimp_preview_area_new ();
|
||
gtk_widget_set_size_request (gm->preview,
|
||
GM_PREVIEW_WIDTH, GM_PREVIEW_HEIGHT);
|
||
|
||
gm->combo = g_object_new (GIMP_TYPE_INT_COMBO_BOX, NULL);
|
||
|
||
g_signal_connect (gm->combo, "changed",
|
||
G_CALLBACK (gm_gradient_combo_callback),
|
||
gm);
|
||
|
||
gm_gradient_combo_fill (gm, default_gradient_name);
|
||
|
||
gtk_widget_show (gm->preview);
|
||
gtk_widget_show (gm->combo);
|
||
|
||
g_signal_connect (gm->combo, "destroy",
|
||
G_CALLBACK (gm_combo_destroy_callback),
|
||
gm);
|
||
|
||
gradient_menus = g_list_append (gradient_menus, gm);
|
||
|
||
return gm;
|
||
}
|
||
|
||
/* Local Functions */
|
||
|
||
static void
|
||
gm_gradient_get_list (void)
|
||
{
|
||
int i;
|
||
|
||
if (gradient_names)
|
||
{
|
||
for (i = 0; i < num_gradient_names; i++)
|
||
g_free (gradient_names[i]);
|
||
g_free (gradient_names);
|
||
}
|
||
|
||
gradient_cache_flush (); /* to make sure */
|
||
gradient_names = gradient_get_list (&num_gradient_names);
|
||
}
|
||
|
||
static void
|
||
gm_gradient_combo_fill (GradientMenu *gm,
|
||
const gchar *default_gradient)
|
||
{
|
||
gint active = 0;
|
||
gint i;
|
||
|
||
for (i = 0; i < num_gradient_names; i++)
|
||
{
|
||
const gchar *name = gradient_names[i];
|
||
|
||
if (strcmp (name, default_gradient) == 0)
|
||
active = i;
|
||
|
||
gimp_int_combo_box_append (GIMP_INT_COMBO_BOX (gm->combo),
|
||
GIMP_INT_STORE_VALUE, i,
|
||
GIMP_INT_STORE_LABEL, name,
|
||
-1);
|
||
}
|
||
|
||
gimp_int_combo_box_set_active (GIMP_INT_COMBO_BOX (gm->combo), active);
|
||
}
|
||
|
||
static void
|
||
gm_gradient_combo_callback (GtkWidget *widget,
|
||
gpointer data)
|
||
{
|
||
GradientMenu *gm = data;
|
||
const gchar *gradient_name;
|
||
gint index;
|
||
|
||
if (! gimp_int_combo_box_get_active (GIMP_INT_COMBO_BOX (widget), &index) ||
|
||
index < 0 ||
|
||
index >= num_gradient_names)
|
||
return;
|
||
|
||
gradient_name = gradient_names[index];
|
||
|
||
DEBUG_PRINT(("gm_combo_callback\n"));
|
||
|
||
gradient_name_copy (gm->gradient_name, gradient_name);
|
||
|
||
gm_preview_draw (gm->preview, gradient_name);
|
||
|
||
if (gm->callback)
|
||
(* gm->callback) (gradient_name, gm->callback_data);
|
||
}
|
||
|
||
static void
|
||
gm_preview_draw (GtkWidget *preview,
|
||
const gchar *gradient_name)
|
||
{
|
||
guchar values[GM_PREVIEW_WIDTH][4];
|
||
gint nvalues = GM_PREVIEW_WIDTH;
|
||
gint row, irow, col;
|
||
guchar dest_row[GM_PREVIEW_WIDTH][3];
|
||
guchar *dest;
|
||
guchar *src;
|
||
gint check, b;
|
||
guchar *dest_total_preview_buffer;
|
||
const gint alpha = 3;
|
||
|
||
gradient_get_values (gradient_name, (guchar *)values, nvalues);
|
||
|
||
dest_total_preview_buffer = g_new (guchar,
|
||
GM_PREVIEW_HEIGHT * GM_PREVIEW_WIDTH * 3);
|
||
|
||
for (row = 0; row < GM_PREVIEW_HEIGHT; row += GIMP_CHECK_SIZE_SM)
|
||
{
|
||
for (col = 0; col < GM_PREVIEW_WIDTH; col++)
|
||
{
|
||
dest = dest_row[col];
|
||
src = values[col];
|
||
|
||
if (src[alpha] == OPAQUE)
|
||
{
|
||
/* no alpha channel or opaque -- simple way */
|
||
for (b = 0; b < alpha; b++)
|
||
dest[b] = src[b];
|
||
}
|
||
else
|
||
{
|
||
/* more or less transparent */
|
||
if((col % (GIMP_CHECK_SIZE) < GIMP_CHECK_SIZE_SM) ^
|
||
(row % (GIMP_CHECK_SIZE) < GIMP_CHECK_SIZE_SM))
|
||
{
|
||
check = GIMP_CHECK_LIGHT * 255;
|
||
}
|
||
else
|
||
{
|
||
check = GIMP_CHECK_DARK * 255;
|
||
}
|
||
|
||
if (src[alpha] == 0)
|
||
{
|
||
/* full transparent -- check */
|
||
for (b = 0; b < alpha; b++)
|
||
dest[b] = check;
|
||
}
|
||
else
|
||
{
|
||
/* middlemost transparent -- mix check and src */
|
||
for (b = 0; b < alpha; b++)
|
||
dest[b] = (src[b] * src[alpha] +
|
||
check * (OPAQUE - src[alpha])) / OPAQUE;
|
||
}
|
||
}
|
||
}
|
||
|
||
for (irow = 0;
|
||
irow < GIMP_CHECK_SIZE_SM && row + irow < GM_PREVIEW_HEIGHT;
|
||
irow++)
|
||
{
|
||
memcpy (dest_total_preview_buffer + (row+irow) * 3 * GM_PREVIEW_WIDTH,
|
||
dest_row,
|
||
GM_PREVIEW_WIDTH * 3);
|
||
}
|
||
}
|
||
|
||
gimp_preview_area_draw (GIMP_PREVIEW_AREA (preview),
|
||
0, 0, GM_PREVIEW_WIDTH, GM_PREVIEW_HEIGHT,
|
||
GIMP_RGB_IMAGE,
|
||
(const guchar *) dest_total_preview_buffer,
|
||
GM_PREVIEW_WIDTH * 3);
|
||
|
||
g_free (dest_total_preview_buffer);
|
||
}
|
||
|
||
static void
|
||
gm_combo_destroy_callback (GtkWidget *w,
|
||
gpointer data)
|
||
{
|
||
GradientMenu *gm = data;
|
||
gradient_menus = g_list_remove (gradient_menus, gm);
|
||
}
|
||
|
||
/*************************************************************************/
|
||
/** **/
|
||
/** +++ Gradients **/
|
||
/** **/
|
||
/*************************************************************************/
|
||
|
||
/*
|
||
Manage both internal and external gradients: list up, cache,
|
||
sampling, etc.
|
||
|
||
External gradients are cached.
|
||
*/
|
||
|
||
|
||
static void
|
||
gradient_name_copy (gchar *dest,
|
||
const gchar *src)
|
||
{
|
||
g_strlcpy (dest, src, GRADIENT_NAME_MAX);
|
||
}
|
||
|
||
/*
|
||
Translate SPACE to "\\040", etc.
|
||
*/
|
||
static void
|
||
gradient_name_encode (gchar *dest,
|
||
const gchar *src)
|
||
{
|
||
gint cnt = GRADIENT_NAME_MAX - 1;
|
||
|
||
while (*src && cnt--)
|
||
{
|
||
if (g_ascii_iscntrl (*src) || g_ascii_isspace (*src) || *src == '\\')
|
||
{
|
||
sprintf (dest, "\\%03o", *src++);
|
||
dest += 4;
|
||
}
|
||
else
|
||
*dest++ = *src++;
|
||
}
|
||
*dest = '\0';
|
||
}
|
||
|
||
/*
|
||
Translate "\\040" to SPACE, etc.
|
||
*/
|
||
static void
|
||
gradient_name_decode (gchar *dest,
|
||
const gchar *src)
|
||
{
|
||
gint cnt = GRADIENT_NAME_MAX - 1;
|
||
guint tmp;
|
||
|
||
while (*src && cnt--)
|
||
{
|
||
if (*src == '\\' && *(src+1) && *(src+2) && *(src+3))
|
||
{
|
||
sscanf (src+1, "%3o", &tmp);
|
||
*dest++ = tmp;
|
||
src += 4;
|
||
}
|
||
else
|
||
*dest++ = *src++;
|
||
}
|
||
*dest = '\0';
|
||
}
|
||
|
||
static void
|
||
gradient_init (void)
|
||
{
|
||
gradient_cache_head = NULL;
|
||
gradient_cache_count = 0;
|
||
}
|
||
|
||
static void
|
||
gradient_free (void)
|
||
{
|
||
gradient_cache_flush ();
|
||
}
|
||
|
||
static gchar **
|
||
gradient_get_list (gint *num_gradients)
|
||
{
|
||
gchar **gradients;
|
||
gchar **external_gradients = NULL;
|
||
gint external_ngradients = 0;
|
||
gint i, n;
|
||
|
||
gradient_cache_flush ();
|
||
external_gradients = gimp_gradients_get_list (NULL);
|
||
external_ngradients = g_strv_length (external_gradients);
|
||
|
||
*num_gradients = G_N_ELEMENTS (internal_gradients) + external_ngradients;
|
||
gradients = g_new (gchar *, *num_gradients);
|
||
|
||
n = 0;
|
||
for (i = 0; i < G_N_ELEMENTS (internal_gradients); i++)
|
||
{
|
||
gradients[n++] = g_strdup (internal_gradients[i]);
|
||
}
|
||
for (i = 0; i < external_ngradients; i++)
|
||
{
|
||
gradients[n++] = g_strdup (external_gradients[i]);
|
||
}
|
||
|
||
g_strfreev (external_gradients);
|
||
|
||
return gradients;
|
||
}
|
||
|
||
static void
|
||
gradient_get_values (const gchar *gradient_name,
|
||
guchar *values,
|
||
gint nvalues)
|
||
{
|
||
/*
|
||
Criteria to distinguish internal and external is rather simple here.
|
||
It should be fixed later.
|
||
*/
|
||
if (gradient_name[0] == '%')
|
||
gradient_get_values_internal (gradient_name, values, nvalues);
|
||
else
|
||
gradient_get_values_external (gradient_name, values, nvalues);
|
||
}
|
||
|
||
static void
|
||
gradient_get_values_internal (const gchar *gradient_name,
|
||
guchar *values,
|
||
gint nvalues)
|
||
{
|
||
const guchar white[4] = { 255, 255, 255, 255 };
|
||
const guchar white_trans[4] = { 255, 255, 255, 0 };
|
||
const guchar red_trans[4] = { 255, 0, 0, 0 };
|
||
const guchar blue_trans[4] = { 0, 0, 255, 0 };
|
||
const guchar yellow_trans[4] = { 255, 255, 0, 0 };
|
||
|
||
/*
|
||
The internal gradients here are example --
|
||
What kind of internals would be useful ?
|
||
*/
|
||
if(!strcmp(gradient_name, "%white"))
|
||
{
|
||
gradient_get_blend (white, white, values, nvalues);
|
||
}
|
||
else if(!strcmp(gradient_name, "%white_grad"))
|
||
{
|
||
gradient_get_blend (white, white_trans, values, nvalues);
|
||
}
|
||
else if (!strcmp (gradient_name, "%red_grad"))
|
||
{
|
||
gradient_get_blend (white, red_trans, values, nvalues);
|
||
}
|
||
else if (!strcmp (gradient_name, "%blue_grad"))
|
||
{
|
||
gradient_get_blend (white, blue_trans, values, nvalues);
|
||
}
|
||
else if (!strcmp (gradient_name, "%yellow_grad"))
|
||
{
|
||
gradient_get_blend (white, yellow_trans, values, nvalues);
|
||
}
|
||
else if (!strcmp (gradient_name, "%random"))
|
||
{
|
||
gradient_get_random (values, nvalues);
|
||
}
|
||
else
|
||
{
|
||
gradient_get_default (gradient_name, values, nvalues);
|
||
}
|
||
}
|
||
|
||
static void
|
||
gradient_get_blend (const guchar *fg,
|
||
const guchar *bg,
|
||
guchar *values,
|
||
gint nvalues)
|
||
{
|
||
gdouble x;
|
||
gint i;
|
||
gint j;
|
||
guchar *v = values;
|
||
|
||
for (i = 0; i < nvalues; i++)
|
||
{
|
||
x = (double) i / nvalues;
|
||
for (j = 0; j < 4; j++)
|
||
*v++ = fg[j] * (1 - x) + bg[j] * x;
|
||
}
|
||
}
|
||
|
||
static void
|
||
gradient_get_random (guchar *values,
|
||
gint nvalues)
|
||
{
|
||
gint i;
|
||
gint j;
|
||
gint inten;
|
||
guchar *v = values;
|
||
|
||
/*
|
||
This is really simple -- gaussian noise might be better
|
||
*/
|
||
for (i = 0; i < nvalues; i++)
|
||
{
|
||
inten = g_random_int_range (0, 256);
|
||
for (j = 0; j < 3; j++)
|
||
*v++ = inten;
|
||
*v++ = 255;
|
||
}
|
||
}
|
||
|
||
static void
|
||
gradient_get_default (const gchar *name,
|
||
guchar *values,
|
||
gint nvalues)
|
||
{
|
||
gdouble e[3];
|
||
gdouble x;
|
||
gint i;
|
||
gint j;
|
||
guchar *v = values;
|
||
|
||
/*
|
||
Create gradient by name
|
||
*/
|
||
name++;
|
||
for (j = 0; j < 3; j++)
|
||
e[j] = name[j] / 255.0;
|
||
|
||
for (i = 0; i < nvalues; i++)
|
||
{
|
||
x = (double) i / nvalues;
|
||
for (j = 0; j < 3; j++)
|
||
*v++ = 255 * pow (x, e[j]);
|
||
*v++ = 255;
|
||
}
|
||
}
|
||
|
||
/*
|
||
Caching gradients is really needed. It really takes 0.2 seconds each
|
||
time to resample an external gradient. (And this plug-in has
|
||
currently 6 gradient menus.)
|
||
|
||
However, this caching routine is not too good. It picks up just
|
||
GRADIENT_RESOLUTION samples every time, and rescales it later. And
|
||
cached values are stored in guchar array. No accuracy.
|
||
*/
|
||
static void
|
||
gradient_get_values_external (const gchar *gradient_name,
|
||
guchar *values,
|
||
gint nvalues)
|
||
{
|
||
GradientCacheItem *ci;
|
||
gboolean found;
|
||
#ifdef DEBUG
|
||
clock_t clk = clock ();
|
||
#endif
|
||
|
||
g_return_if_fail (nvalues >= 2);
|
||
|
||
ci = gradient_cache_lookup (gradient_name, &found);
|
||
if (!found)
|
||
{
|
||
/* FIXME: "reverse" hardcoded to FALSE. */
|
||
gradient_get_values_real_external (gradient_name, ci->values,
|
||
GRADIENT_RESOLUTION, FALSE);
|
||
}
|
||
if (nvalues == GRADIENT_RESOLUTION)
|
||
{
|
||
memcpy (values, ci->values, 4 * GRADIENT_RESOLUTION);
|
||
}
|
||
else
|
||
{
|
||
double pos, frac;
|
||
int ipos;
|
||
int i, j;
|
||
|
||
for (i = 0; i < nvalues; i++)
|
||
{
|
||
pos = ((double) i / (nvalues - 1)) * (GRADIENT_RESOLUTION - 1);
|
||
g_assert (0 <= pos && pos <= GRADIENT_RESOLUTION - 1);
|
||
ipos = (int) pos; frac = pos - ipos;
|
||
if (frac == 0.0)
|
||
{
|
||
memcpy (&values[4 * i], &ci->values[4 * ipos], 4);
|
||
}
|
||
else
|
||
for (j = 0; j < 4; j++)
|
||
values[4 * i + j] = ci->values[4 * ipos + j] * (1 - frac)
|
||
+ ci->values[4 * (ipos + 1) + j] * frac;
|
||
}
|
||
}
|
||
|
||
#ifdef DEBUG
|
||
get_values_external_clock += clock () - clk;
|
||
get_values_external_count ++;
|
||
#endif
|
||
|
||
}
|
||
|
||
static void
|
||
gradient_get_values_real_external (const gchar *gradient_name,
|
||
guchar *values,
|
||
gint nvalues,
|
||
gboolean reverse)
|
||
{
|
||
GimpGradient *gradient;
|
||
gint n_tmp_values;
|
||
gdouble *tmp_values;
|
||
gint i;
|
||
gint j;
|
||
|
||
gradient = gimp_gradient_get_by_name (gradient_name);
|
||
|
||
gimp_gradient_get_uniform_samples (gradient, nvalues, reverse,
|
||
&n_tmp_values, &tmp_values);
|
||
|
||
for (i = 0; i < nvalues; i++)
|
||
for (j = 0; j < 4; j++)
|
||
values[4 * i + j] = (guchar) (tmp_values[4 * i + j] * 255);
|
||
|
||
g_free (tmp_values);
|
||
}
|
||
|
||
void
|
||
gradient_cache_flush (void)
|
||
{
|
||
GradientCacheItem *ci;
|
||
GradientCacheItem *tmp;
|
||
|
||
ci = gradient_cache_head;
|
||
while (ci)
|
||
{
|
||
tmp = ci->next;
|
||
g_free (ci);
|
||
ci = tmp;
|
||
}
|
||
gradient_cache_head = NULL;
|
||
gradient_cache_count = 0;
|
||
}
|
||
|
||
static GradientCacheItem *
|
||
gradient_cache_lookup (const gchar *name,
|
||
gboolean *found)
|
||
{
|
||
GradientCacheItem *ci;
|
||
|
||
ci = gradient_cache_head;
|
||
while (ci)
|
||
{
|
||
if (!strcmp (ci->name, name))
|
||
break;
|
||
ci = ci->next;
|
||
}
|
||
if (ci)
|
||
{
|
||
*found = TRUE;
|
||
if (!ci->prev)
|
||
{
|
||
g_assert (ci == gradient_cache_head);
|
||
return ci;
|
||
}
|
||
ci->prev->next = ci->next;
|
||
if (ci->next)
|
||
ci->next->prev = ci->prev;
|
||
ci->next = gradient_cache_head;
|
||
gradient_cache_head->prev = ci;
|
||
gradient_cache_head = ci;
|
||
ci->prev = NULL;
|
||
return ci;
|
||
}
|
||
else
|
||
{
|
||
*found = FALSE;
|
||
while (gradient_cache_count >= GRADIENT_CACHE_SIZE)
|
||
gradient_cache_zorch();
|
||
ci = g_new (GradientCacheItem, 1);
|
||
g_strlcpy (ci->name, name, GRADIENT_NAME_MAX);
|
||
ci->next = gradient_cache_head;
|
||
ci->prev = NULL;
|
||
if (gradient_cache_head)
|
||
gradient_cache_head->prev = ci;
|
||
gradient_cache_head = ci;
|
||
++gradient_cache_count;
|
||
return ci;
|
||
}
|
||
}
|
||
|
||
static void
|
||
gradient_cache_zorch (void)
|
||
{
|
||
GradientCacheItem *ci = gradient_cache_head;
|
||
|
||
while (ci && ci->next)
|
||
{
|
||
ci = ci->next;
|
||
}
|
||
if (ci)
|
||
{
|
||
g_assert (ci->next == NULL);
|
||
if (ci->prev)
|
||
ci->prev->next = NULL;
|
||
else
|
||
gradient_cache_head = NULL;
|
||
g_free (ci);
|
||
--gradient_cache_count;
|
||
}
|
||
}
|
||
|
||
static void
|
||
gradient_scale_entry_update_double (GimpLabelSpin *entry,
|
||
gdouble *value)
|
||
{
|
||
*value = gimp_label_spin_get_value (entry);
|
||
}
|
||
|
||
static void
|
||
gradient_scale_entry_update_int (GimpLabelSpin *entry,
|
||
gint *value)
|
||
{
|
||
*value = (gint) gimp_label_spin_get_value (entry);
|
||
}
|
||
|
||
#ifdef DEBUG
|
||
void
|
||
gradient_report (void)
|
||
{
|
||
double total = (double) get_values_external_clock / CLOCKS_PER_SEC;
|
||
|
||
g_printerr ("gradient_get_values_external "
|
||
"%.2f sec. / %d times (ave %.2f sec.)\n",
|
||
total,
|
||
get_values_external_count,
|
||
total / get_values_external_count);
|
||
}
|
||
#endif
|