/* The GIMP -- an image manipulation program * Copyright (C) 1995 Spencer Kimball and Peter Mattis * Compose plug-in (C) 1997,1999 Peter Kirchgessner * e-mail: peter@kirchgessner.net, WWW: http://www.kirchgessner.net * * 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 2 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, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* * This plug-in composes RGB-images from several types of channels */ /* Event history: * V 1.00, PK, 29-Jul-97, Creation * V 1.01, nn, 20-Dec-97, Add default case in switch for hsv_to_rgb () * V 1.02, PK, 18-Sep-98, Change variable names in Parameter definition. * Otherwise script-fu merges parameters (reported by Patrick Valsecchi) * Check images for same width/height (interactive mode) * Use drawables in interactive menu * Use g_message in interactive mode * Check sensitivity of menues (thanks to Kevin Turner, * kevint@poboxes.com) * V1.03, PK, 17-Mar-99, Update for GIMP 1.1.3 * Allow image ID 0 * Prepare for localization */ static char ident[] = "@(#) GIMP Compose plug-in v1.03 17-Mar-99"; #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include "gtk/gtk.h" #include "libgimp/gimp.h" #include "libgimp/gimpui.h" #include "libgimp/stdplugins-intl.h" /* Declare local functions */ static void query (void); static void run (char *name, int nparams, GParam *param, int *nreturn_vals, GParam **return_vals); static void show_message (const char *message); static gint32 compose (char *compose_type, gint32 *compose_ID, int compose_by_drawable); static gint32 create_new_image (char *filename, guint width, guint height, GDrawableType gdtype, gint32 *layer_ID, GDrawable **drawable, GPixelRgn *pixel_rgn); static int cmp_icase (char *s1, char *s2); static void compose_rgb (unsigned char **src, int *incr, int numpix, unsigned char *dst); static void compose_rgba (unsigned char **src, int *incr, int numpix, unsigned char *dst); static void compose_hsv (unsigned char **src, int *incr, int numpix, unsigned char *dst); static void compose_cmy (unsigned char **src, int *incr, int numpix, unsigned char *dst); static void compose_cmyk (unsigned char **src, int *incr, int numpix, unsigned char *dst); static void hsv_to_rgb (unsigned char *h, unsigned char *s, unsigned char *v, unsigned char *rgb); static gint compose_dialog (char *compose_type, gint32 drawable_ID); static gint check_gray (gint32 image_id, gint32 drawable_id, gpointer data); static void image_menu_callback (gint32 id, gpointer data); static void compose_close_callback (GtkWidget *widget, gpointer data); static void compose_ok_callback (GtkWidget *widget, gpointer data); static void compose_type_toggle_update (GtkWidget *widget, gpointer data); /* Maximum number of images to compose */ #define MAX_COMPOSE_IMAGES 4 /* Description of a composition */ typedef struct { char *compose_type; /* Type of composition ("RGB", "RGBA",...) */ int num_images; /* Number of input images needed */ char *channel_name[MAX_COMPOSE_IMAGES]; /* channel names for dialog */ char *filename; /* Name of new image */ /* Compose functon */ void (*compose_fun)(unsigned char **src, int *incr_src, int numpix, unsigned char *dst); } COMPOSE_DSC; /* Array of available compositions. */ #define CHNL_NA "-" static COMPOSE_DSC compose_dsc[] = { { N_("RGB"), 3, { N_("Red:"), N_("Green:"), N_("Blue:"), CHNL_NA }, N_("rgb-compose"), compose_rgb }, { N_("RGBA"), 4, { N_("Red:"), N_("Green:"), N_("Blue:"),N_("Alpha:") }, N_("rgba-compose"), compose_rgba }, { N_("HSV"), 3, { N_("Hue:"), N_("Saturation:"), N_("Value:"), CHNL_NA }, N_("hsv-compose"), compose_hsv }, { N_("CMY"), 3, { N_("Cyan:"), N_("Magenta:"), N_("Yellow:"), CHNL_NA }, N_("cmy-compose"), compose_cmy }, { N_("CMYK"), 4, { N_("Cyan:"), N_("Magenta:"),N_("Yellow:"),N_("Black:")}, N_("cmyk-compose"), compose_cmyk } }; #define MAX_COMPOSE_TYPES (sizeof (compose_dsc) / sizeof (compose_dsc[0])) typedef struct { gint32 compose_ID[MAX_COMPOSE_IMAGES]; /* Image IDs of input images */ char compose_type[32]; /* type of composition */ } ComposeVals; /* Dialog structure */ typedef struct { int width, height; /* Size of selected image */ GtkWidget *channel_label[MAX_COMPOSE_IMAGES]; /* The labels to change */ GtkWidget *channel_menu[MAX_COMPOSE_IMAGES]; /* The menues */ gint32 select_ID[MAX_COMPOSE_IMAGES]; /* Image Ids selected by menu */ gint compose_flag[MAX_COMPOSE_TYPES]; /* toggle data of compose type */ gint run; } ComposeInterface; GPlugInInfo PLUG_IN_INFO = { NULL, /* init_proc */ NULL, /* quit_proc */ query, /* query_proc */ run, /* run_proc */ }; static ComposeVals composevals = { { 0 }, /* Image IDs of images to compose */ "rgb" /* Type of composition */ }; static ComposeInterface composeint = { 0, 0, /* width, height */ { NULL }, /* Label Widgets */ { NULL }, /* Menu Widgets */ { 0 }, /* Image IDs from menues */ { 0 }, /* Compose type toggle flags */ FALSE /* run */ }; static GRunModeType run_mode; MAIN () static void query () { static GParamDef args[] = { { PARAM_INT32, "run_mode", "Interactive, non-interactive" }, { PARAM_IMAGE, "image1", "First input image" }, { PARAM_DRAWABLE, "drawable", "Input drawable (not used)" }, { PARAM_IMAGE, "image2", "Second input image" }, { PARAM_IMAGE, "image3", "Third input image" }, { PARAM_IMAGE, "image4", "Fourth input image" }, { PARAM_STRING, "compose_type", "What to compose: RGB, RGBA, HSV,\ CMY, CMYK" } }; static GParamDef return_vals[] = { { PARAM_IMAGE, "new_image", "Output image" } }; static int nargs = sizeof (args) / sizeof (args[0]); static int nreturn_vals = sizeof (return_vals) / sizeof (return_vals[0]); static GParamDef drw_args[] = { { PARAM_INT32, "run_mode", "Interactive, non-interactive" }, { PARAM_IMAGE, "image1", "First input image (not used)" }, { PARAM_DRAWABLE, "drawable1", "First input drawable" }, { PARAM_DRAWABLE, "drawable2", "Second input drawable" }, { PARAM_DRAWABLE, "drawable3", "Third input drawable" }, { PARAM_DRAWABLE, "drawable4", "Fourth input drawable" }, { PARAM_STRING, "compose_type", "What to compose: RGB, RGBA, HSV,\ CMY, CMYK" } }; static GParamDef drw_return_vals[] = { { PARAM_IMAGE, "new_image", "Output image" } }; static int drw_nargs = sizeof (args) / sizeof (args[0]); static int drw_nreturn_vals = sizeof (return_vals) / sizeof (return_vals[0]); INIT_I18N (); gimp_install_procedure ("plug_in_compose", _("Compose an image from multiple gray images"), _("This function creates a new image from\ multiple gray images"), "Peter Kirchgessner", "Peter Kirchgessner (peter@kirchgessner.net)", "1997", N_("/Image/Mode/Compose..."), "GRAY*", PROC_PLUG_IN, nargs, nreturn_vals, args, return_vals); gimp_install_procedure ("plug_in_drawable_compose", _("Compose an image from multiple drawables of gray images"), _("This function creates a new image from\ multiple drawables of gray images"), "Peter Kirchgessner", "Peter Kirchgessner (peter@kirchgessner.net)", "1998", NULL, /* It is not available in interactive mode */ "GRAY*", PROC_PLUG_IN, drw_nargs, drw_nreturn_vals, drw_args, drw_return_vals); } static void show_message (const char *message) { if (run_mode == RUN_INTERACTIVE) gimp_message (message); else printf ("%s\n", message); } static void run (char *name, int nparams, GParam *param, int *nreturn_vals, GParam **return_vals) { static GParam values[2]; GStatusType status = STATUS_SUCCESS; gint32 image_ID, drawable_ID; int compose_by_drawable; char msg[256]; INIT_I18N_UI (); run_mode = param[0].data.d_int32; compose_by_drawable = (strcmp (name, "plug_in_drawable_compose") == 0); *nreturn_vals = 2; *return_vals = values; values[0].type = PARAM_STATUS; values[0].data.d_status = status; values[1].type = PARAM_IMAGE; values[1].data.d_int32 = -1; switch (run_mode) { case RUN_INTERACTIVE: /* Possibly retrieve data */ gimp_get_data (name , &composevals); /* The dialog is now drawable based. Get a drawable-ID of the image */ if (strcmp (name, "plug_in_compose") == 0) {gint32 *layer_list; gint nlayers; layer_list = gimp_image_get_layers (param[1].data.d_int32, &nlayers); if ((layer_list == NULL) || (nlayers <= 0)) { sprintf (msg, _("compose: Could not get layers for image %d"), (int)param[1].data.d_int32); show_message (msg); return; } drawable_ID = layer_list[0]; g_free (layer_list); } else drawable_ID = param[2].data.d_int32; compose_by_drawable = 1; /* First acquire information with a dialog */ if (! compose_dialog (composevals.compose_type, drawable_ID)) return; break; case RUN_NONINTERACTIVE: /* Make sure all the arguments are there! */ if (nparams != 7) status = STATUS_CALLING_ERROR; if (status == STATUS_SUCCESS) { composevals.compose_ID[0] = compose_by_drawable ? param[2].data.d_int32 : param[1].data.d_int32; composevals.compose_ID[1] = param[3].data.d_int32; composevals.compose_ID[2] = param[4].data.d_int32; composevals.compose_ID[3] = param[5].data.d_int32; strncpy (composevals.compose_type, param[6].data.d_string, sizeof (composevals.compose_type)); composevals.compose_type[sizeof (composevals.compose_type)-1] = '\0'; } break; case RUN_WITH_LAST_VALS: /* Possibly retrieve data */ gimp_get_data (name, &composevals); break; default: break; } if (status == STATUS_SUCCESS) { if (run_mode != RUN_NONINTERACTIVE) gimp_progress_init (_("Composing...")); image_ID = compose (composevals.compose_type, composevals.compose_ID, compose_by_drawable); if (image_ID < 0) { status = STATUS_EXECUTION_ERROR; } else { values[1].data.d_int32 = image_ID; gimp_image_undo_enable (image_ID); gimp_image_clean_all (image_ID); if (run_mode != RUN_NONINTERACTIVE) gimp_display_new (image_ID); } /* Store data */ if (run_mode == RUN_INTERACTIVE) gimp_set_data (name, &composevals, sizeof (ComposeVals)); } values[0].data.d_status = status; } /* Compose an image from several gray-images */ static gint32 compose (char *compose_type, gint32 *compose_ID, int compose_by_drawable) { int width, height, tile_height, scan_lines; int num_images, compose_idx, incr_src[MAX_COMPOSE_IMAGES]; int i, j; gint num_layers; gint32 layer_ID_dst, image_ID_dst; unsigned char *src[MAX_COMPOSE_IMAGES], *dst = (unsigned char *)ident; GDrawableType gdtype_dst; GDrawable *drawable_src[MAX_COMPOSE_IMAGES], *drawable_dst; GPixelRgn pixel_rgn_src[MAX_COMPOSE_IMAGES], pixel_rgn_dst; /* Search type of composing */ compose_idx = -1; for (j = 0; j < MAX_COMPOSE_TYPES; j++) { if (cmp_icase (compose_type, compose_dsc[j].compose_type) == 0) compose_idx = j; } if (compose_idx < 0) return (-1); num_images = compose_dsc[compose_idx].num_images; tile_height = gimp_tile_height (); /* Check image sizes */ if (compose_by_drawable) { width = gimp_drawable_width (compose_ID[0]); height = gimp_drawable_height (compose_ID[0]); for (j = 1; j < num_images; j++) { if ( (width != (int)gimp_drawable_width (compose_ID[j])) || (height != (int)gimp_drawable_height (compose_ID[j]))) { show_message (_("compose: drawables have different size")); return -1; } } for (j = 0; j < num_images; j++) drawable_src[j] = gimp_drawable_get (compose_ID[j]); } else /* Compose by image ID */ { width = gimp_image_width (compose_ID[0]); height = gimp_image_height (compose_ID[0]); for (j = 1; j < num_images; j++) { if ( (width != (int)gimp_image_width (compose_ID[j])) || (height != (int)gimp_image_height (compose_ID[j]))) { show_message (_("compose: images have different size")); return -1; } } /* Get first layer/drawable for all input images */ for (j = 0; j < num_images; j++) {gint32 *g32; /* Get first layer of image */ g32 = gimp_image_get_layers (compose_ID[j], &num_layers); if ((g32 == NULL) || (num_layers <= 0)) { show_message (_("compose: error in getting layer IDs")); return (-1); } /* Get drawable for layer */ drawable_src[j] = gimp_drawable_get (g32[0]); g_free (g32); } } /* Get pixel region for all input drawables */ for (j = 0; j < num_images; j++) { /* Check bytes per pixel */ incr_src[j] = drawable_src[j]->bpp; if ((incr_src[j] != 1) && (incr_src[j] != 2)) {char msg[256]; sprintf (msg, _("compose: image is not a gray image (bpp=%d)"), incr_src[j]); show_message (msg); return (-1); } /* Get pixel region */ gimp_pixel_rgn_init (&(pixel_rgn_src[j]), drawable_src[j], 0, 0, width, height, FALSE, FALSE); /* Get memory for retrieving information */ src[j] = (unsigned char *)g_malloc (tile_height * width * drawable_src[j]->bpp); if (src[j] == NULL) { show_message (_("compose: not enough memory")); return (-1); } } /* Create new image */ gdtype_dst = (compose_dsc[compose_idx].compose_fun == compose_rgba) ? RGBA_IMAGE : RGB_IMAGE; image_ID_dst = create_new_image (compose_dsc[compose_idx].filename, width, height, gdtype_dst, &layer_ID_dst, &drawable_dst, &pixel_rgn_dst); dst = (unsigned char *)g_malloc (tile_height * width * drawable_dst->bpp); if (dst == NULL) { for (j = 0; j < num_images; j++) g_free (src[j]); show_message (_("compose: not enough memory")); return (-1); } /* Do the composition */ i = 0; while (i < height) { scan_lines = (i+tile_height-1 < height) ? tile_height : (height-i); /* Get source pixel regions */ for (j = 0; j < num_images; j++) gimp_pixel_rgn_get_rect (&(pixel_rgn_src[j]), src[j], 0, i, width, scan_lines); /* Do the composition */ compose_dsc[compose_idx].compose_fun (src,incr_src,width*tile_height,dst); /* Set destination pixel region */ gimp_pixel_rgn_set_rect (&pixel_rgn_dst, dst, 0, i, width, scan_lines); i += scan_lines; if (run_mode != RUN_NONINTERACTIVE) gimp_progress_update (((double)i) / (double)height); } for (j = 0; j < num_images; j++) { g_free (src[j]); gimp_drawable_flush (drawable_src[j]); gimp_drawable_detach (drawable_src[j]); } g_free (dst); gimp_drawable_flush (drawable_dst); gimp_drawable_detach (drawable_dst); return image_ID_dst; } /* Create an image. Sets layer_ID, drawable and rgn. Returns image_ID */ static gint32 create_new_image (char *filename, guint width, guint height, GDrawableType gdtype, gint32 *layer_ID, GDrawable **drawable, GPixelRgn *pixel_rgn) { gint32 image_ID; GImageType gitype; if ((gdtype == GRAY_IMAGE) || (gdtype == GRAYA_IMAGE)) gitype = GRAY; else if ((gdtype == INDEXED_IMAGE) || (gdtype == INDEXEDA_IMAGE)) gitype = INDEXED; else gitype = RGB; image_ID = gimp_image_new (width, height, gitype); gimp_image_set_filename (image_ID, filename); *layer_ID = gimp_layer_new (image_ID, _("Background"), width, height, gdtype, 100, NORMAL_MODE); gimp_image_add_layer (image_ID, *layer_ID, 0); *drawable = gimp_drawable_get (*layer_ID); gimp_pixel_rgn_init (pixel_rgn, *drawable, 0, 0, (*drawable)->width, (*drawable)->height, TRUE, FALSE); return (image_ID); } /* Compare two strings ignoring case (could also be done by strcasecmp() */ /* but is it available everywhere ?) */ static int cmp_icase (char *s1, char *s2) { int c1, c2; c1 = toupper (*s1); c2 = toupper (*s2); while (*s1 && *s2) { if (c1 != c2) return (c2 - c1); c1 = toupper (*(++s1)); c2 = toupper (*(++s2)); } return (c2 - c1); } static void compose_rgb (unsigned char **src, int *incr_src, int numpix, unsigned char *dst) { register unsigned char *red_src = src[0]; register unsigned char *green_src = src[1]; register unsigned char *blue_src = src[2]; register unsigned char *rgb_dst = dst; register int count = numpix; int red_incr = incr_src[0], green_incr = incr_src[1], blue_incr = incr_src[2]; if ((red_incr == 1) && (green_incr == 1) && (blue_incr == 1)) { while (count-- > 0) { *(rgb_dst++) = *(red_src++); *(rgb_dst++) = *(green_src++); *(rgb_dst++) = *(blue_src++); } } else { while (count-- > 0) { *(rgb_dst++) = *red_src; red_src += red_incr; *(rgb_dst++) = *green_src; green_src += green_incr; *(rgb_dst++) = *blue_src; blue_src += blue_incr; } } } static void compose_rgba (unsigned char **src, int *incr_src, int numpix, unsigned char *dst) { register unsigned char *red_src = src[0]; register unsigned char *green_src = src[1]; register unsigned char *blue_src = src[2]; register unsigned char *alpha_src = src[3]; register unsigned char *rgb_dst = dst; register int count = numpix; int red_incr = incr_src[0], green_incr = incr_src[1], blue_incr = incr_src[2], alpha_incr = incr_src[3]; if ( (red_incr == 1) && (green_incr == 1) && (blue_incr == 1) && (alpha_incr == 1)) { while (count-- > 0) { *(rgb_dst++) = *(red_src++); *(rgb_dst++) = *(green_src++); *(rgb_dst++) = *(blue_src++); *(rgb_dst++) = *(alpha_src++); } } else { while (count-- > 0) { *(rgb_dst++) = *red_src; red_src += red_incr; *(rgb_dst++) = *green_src; green_src += green_incr; *(rgb_dst++) = *blue_src; blue_src += blue_incr; *(rgb_dst++) = *alpha_src; alpha_src += alpha_incr; } } } static void compose_hsv (unsigned char **src, int *incr_src, int numpix, unsigned char *dst) { register unsigned char *hue_src = src[0]; register unsigned char *sat_src = src[1]; register unsigned char *val_src = src[2]; register unsigned char *rgb_dst = dst; register int count = numpix; int hue_incr = incr_src[0], sat_incr = incr_src[1], val_incr = incr_src[2]; while (count-- > 0) { hsv_to_rgb (hue_src, sat_src, val_src, rgb_dst); rgb_dst += 3; hue_src += hue_incr; sat_src += sat_incr; val_src += val_incr; } } static void compose_cmy (unsigned char **src, int *incr_src, int numpix, unsigned char *dst) { register unsigned char *cyan_src = src[0]; register unsigned char *magenta_src = src[1]; register unsigned char *yellow_src = src[2]; register unsigned char *rgb_dst = dst; register int count = numpix; int cyan_incr = incr_src[0], magenta_incr = incr_src[1], yellow_incr = incr_src[2]; if ((cyan_incr == 1) && (magenta_incr == 1) && (yellow_incr == 1)) { while (count-- > 0) { *(rgb_dst++) = 255 - *(cyan_src++); *(rgb_dst++) = 255 - *(magenta_src++); *(rgb_dst++) = 255 - *(yellow_src++); } } else { while (count-- > 0) { *(rgb_dst++) = 255 - *cyan_src; *(rgb_dst++) = 255 - *magenta_src; *(rgb_dst++) = 255 - *yellow_src; cyan_src += cyan_incr; magenta_src += magenta_incr; yellow_src += yellow_incr; } } } static void compose_cmyk (unsigned char **src, int *incr_src, int numpix, unsigned char *dst) { register unsigned char *cyan_src = src[0]; register unsigned char *magenta_src = src[1]; register unsigned char *yellow_src = src[2]; register unsigned char *black_src = src[3]; register unsigned char *rgb_dst = dst; register int count = numpix; int cyan, magenta, yellow, black; int cyan_incr = incr_src[0], magenta_incr = incr_src[1], yellow_incr = incr_src[2], black_incr = incr_src[3]; while (count-- > 0) { black = (int)*black_src; if (black) { cyan = (int)*cyan_src; magenta = (int)*magenta_src; yellow = (int)*yellow_src; cyan += black; if (cyan > 255) cyan = 255; magenta += black; if (magenta > 255) magenta = 255; yellow += black; if (yellow > 255) yellow = 255; *(rgb_dst++) = 255 - cyan; *(rgb_dst++) = 255 - magenta; *(rgb_dst++) = 255 - yellow; } else { *(rgb_dst++) = 255 - *cyan_src; *(rgb_dst++) = 255 - *magenta_src; *(rgb_dst++) = 255 - *yellow_src; } cyan_src += cyan_incr; magenta_src += magenta_incr; yellow_src += yellow_incr; black_src += black_incr; } } static gint compose_dialog (char *compose_type, gint32 drawable_ID) { GtkWidget *dlg; GtkWidget *button; GtkWidget *toggle; GtkWidget *left_frame, *right_frame; GtkWidget *left_vbox, *right_vbox; GtkWidget *hbox; GtkWidget *label; GtkWidget *table; GtkWidget *image_option_menu, *image_menu; GSList *group; gchar **argv; gint argc; int j, compose_idx, sensitive; /* Check default compose type */ compose_idx = -1; for (j = 0; j < MAX_COMPOSE_TYPES; j++) { if (cmp_icase (compose_type, compose_dsc[j].compose_type) == 0) compose_idx = j; } if (compose_idx < 0) compose_idx = 0; /* Save original image width/height */ composeint.width = gimp_drawable_width (drawable_ID); composeint.height = gimp_drawable_height (drawable_ID); argc = 1; argv = g_new (gchar *, 1); argv[0] = g_strdup ("Compose"); gtk_init (&argc, &argv); gtk_rc_parse (gimp_gtkrc ()); dlg = gtk_dialog_new (); gtk_window_set_title (GTK_WINDOW (dlg), _("Compose")); gtk_window_set_position (GTK_WINDOW (dlg), GTK_WIN_POS_MOUSE); gtk_signal_connect (GTK_OBJECT (dlg), "destroy", (GtkSignalFunc) compose_close_callback, NULL); /* Action area */ button = gtk_button_new_with_label (_("OK")); GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT); gtk_signal_connect (GTK_OBJECT (button), "clicked", (GtkSignalFunc) compose_ok_callback, dlg); gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dlg)->action_area), button, TRUE, TRUE, 0); gtk_widget_grab_default (button); gtk_widget_show (button); button = gtk_button_new_with_label (_("Cancel")); GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT); gtk_signal_connect_object (GTK_OBJECT (button), "clicked", (GtkSignalFunc) gtk_widget_destroy, GTK_OBJECT (dlg)); gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dlg)->action_area), button, TRUE, TRUE, 0); gtk_widget_show (button); /* parameter settings */ hbox = gtk_hbox_new (FALSE, 0); gtk_container_set_border_width (GTK_CONTAINER (hbox), 0); gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dlg)->vbox), hbox, TRUE, TRUE, 0); gtk_widget_show (hbox); /* The left frame keeps the compose type toggles */ left_frame = gtk_frame_new (_("Compose channels:")); gtk_frame_set_shadow_type (GTK_FRAME (left_frame), GTK_SHADOW_ETCHED_IN); gtk_container_set_border_width (GTK_CONTAINER (left_frame), 5); gtk_box_pack_start (GTK_BOX (hbox), left_frame, TRUE, TRUE, 0); left_vbox = gtk_vbox_new (FALSE, 5); gtk_container_set_border_width (GTK_CONTAINER (left_vbox), 5); gtk_container_add (GTK_CONTAINER (left_frame), left_vbox); /* The right frame keeps the selection menues for images. */ /* Because the labels within this frame will change when a toggle */ /* in the left frame is changed, fill in the right part first. */ /* Otherwise it can occur, that a non-existing label might be changed. */ right_frame = gtk_frame_new (_("Channel representations:")); gtk_frame_set_shadow_type (GTK_FRAME (right_frame), GTK_SHADOW_ETCHED_IN); gtk_container_set_border_width (GTK_CONTAINER (right_frame), 5); gtk_box_pack_start (GTK_BOX (hbox), right_frame, TRUE, TRUE, 0); right_vbox = gtk_vbox_new (FALSE, 5); gtk_container_set_border_width (GTK_CONTAINER (right_vbox), 5); gtk_container_add (GTK_CONTAINER (right_frame), right_vbox); table = gtk_table_new (MAX_COMPOSE_IMAGES, 3, FALSE); gtk_table_set_row_spacings (GTK_TABLE (table), 5); gtk_table_set_col_spacings (GTK_TABLE (table), 5); gtk_box_pack_start (GTK_BOX (right_vbox), table, TRUE, TRUE, 0); gtk_widget_show (table); /* Channel names */ for (j = 0; j < MAX_COMPOSE_IMAGES; j++) { composeint.channel_label[j] = label = gtk_label_new (compose_dsc[compose_idx].channel_name[j]); gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5); gtk_table_attach (GTK_TABLE (table), label, 1, 2, j, j+1, GTK_FILL, GTK_FILL, 0, 0); gtk_widget_show (label); } /* Set sensitivity of last label */ sensitive = (strcmp (compose_dsc[compose_idx].channel_name[3], CHNL_NA) != 0); gtk_widget_set_sensitive (composeint.channel_label[3], sensitive); /* Menues to select images */ for (j = 0; j < MAX_COMPOSE_IMAGES; j++) { composeint.select_ID[j] = drawable_ID; composeint.channel_menu[j] = image_option_menu = gtk_option_menu_new (); image_menu = gimp_drawable_menu_new (check_gray, image_menu_callback, &(composeint.select_ID[j]), composeint.select_ID[j]); gtk_table_attach (GTK_TABLE (table), image_option_menu, 2, 3, j, j+1, GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0); gtk_widget_show (image_option_menu); gtk_option_menu_set_menu (GTK_OPTION_MENU (image_option_menu), image_menu); } gtk_widget_set_sensitive (composeint.channel_menu[3], sensitive); /* Compose types */ group = NULL; for (j = 0; j < MAX_COMPOSE_TYPES; j++) { toggle = gtk_radio_button_new_with_label (group, gettext(compose_dsc[j].compose_type)); group = gtk_radio_button_group (GTK_RADIO_BUTTON (toggle)); gtk_box_pack_start (GTK_BOX (left_vbox), toggle, TRUE, TRUE, 0); composeint.compose_flag[j] = (j == compose_idx); gtk_signal_connect (GTK_OBJECT (toggle), "toggled", (GtkSignalFunc) compose_type_toggle_update, &(composeint.compose_flag[j])); gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), composeint.compose_flag[j]); gtk_widget_show (toggle); } gtk_widget_show (left_vbox); gtk_widget_show (right_vbox); gtk_widget_show (left_frame); gtk_widget_show (right_frame); gtk_widget_show (dlg); gtk_main (); gdk_flush (); return composeint.run; } /* hsv_to_rgb has been taken from the compose-plug-in of GIMP V 0.54 * and hass been modified a little bit */ static void hsv_to_rgb (unsigned char *h, unsigned char *s, unsigned char *v, unsigned char *rgb) { double hue, sat, val; double f, p, q, t; int red, green, blue; if (*s == 0) { rgb[0] = rgb[1] = rgb[2] = *v; } else { hue = *h * 6.0 / 255.0; sat = *s / 255.0; val = *v / 255.0; f = hue - (int) hue; p = val * (1.0 - sat); q = val * (1.0 - (sat * f)); t = val * (1.0 - (sat * (1.0 - f))); switch ((int) hue) { case 0: red = (int)(val * 255.0); green = (int)(t * 255.0); blue = (int)(p * 255.0); break; case 1: red = (int)(q * 255.0); green = (int)(val * 255.0); blue = (int)(p * 255.0); break; case 2: red = (int)(p * 255.0); green = (int)(val * 255.0); blue = (int)(t * 255.0); break; case 3: red = (int)(p * 255.0); green = (int)(q * 255.0); blue = (int)(val * 255.0); break; case 4: red = (int)(t * 255.0); green = (int)(p * 255.0); blue = (int)(val * 255.0); break; case 5: red = (int)(val * 255.0); green = (int)(p * 255.0); blue = (int)(q * 255.0); break; default: red = 0; green = 0; blue = 0; break; } if (red < 0) red = 0; else if (red > 255) red = 255; if (green < 0) green = 0; else if (green > 255) green = 255; if (blue < 0) blue = 0; else if (blue > 255) blue = 255; rgb[0] = (unsigned char)red; rgb[1] = (unsigned char)green; rgb[2] = (unsigned char)blue; } } /* Compose interface functions */ static gint check_gray (gint32 image_id, gint32 drawable_id, gpointer data) { return ((gimp_image_base_type (image_id) == GRAY) && (gimp_image_width (image_id) == composeint.width) && (gimp_image_height (image_id) == composeint.height)); } static void image_menu_callback (gint32 id, gpointer data) { *(gint32 *)data = id; } static void compose_close_callback (GtkWidget *widget, gpointer data) { gtk_main_quit (); } static void compose_ok_callback (GtkWidget *widget, gpointer data) {int j; composeint.run = TRUE; gtk_widget_destroy (GTK_WIDGET (data)); for (j = 0; j < MAX_COMPOSE_IMAGES; j++) composevals.compose_ID[j] = composeint.select_ID[j]; for (j = 0; j < MAX_COMPOSE_TYPES; j++) { if (composeint.compose_flag[j]) { strcpy (composevals.compose_type, compose_dsc[j].compose_type); break; } } } static void compose_type_toggle_update (GtkWidget *widget, gpointer data) { gint *toggle_val; gint compose_idx, j; int sensitive; toggle_val = (gint *) data; if (GTK_TOGGLE_BUTTON (widget)->active) { *toggle_val = TRUE; compose_idx = toggle_val - &(composeint.compose_flag[0]); for (j = 0; j < MAX_COMPOSE_IMAGES; j++) gtk_label_set_text (GTK_LABEL (composeint.channel_label[j]), compose_dsc[compose_idx].channel_name[j]); /* Set sensitivity of last label */ sensitive = (strcmp (compose_dsc[compose_idx].channel_name[3], CHNL_NA) != 0); gtk_widget_set_sensitive (composeint.channel_label[3], sensitive); gtk_widget_set_sensitive (composeint.channel_menu[3], sensitive); } else *toggle_val = FALSE; }