1999-11-20 Michael Natterer <mitch@gimp.org> Reorganized the core menu items (everything except <Image>/Filters). Everything is of course trivial to change again, so please comment on the new "menu feeling" ;-) * app/menus.[ch]: - Applied the suggestions collected by Olof. - Added "..." to all items which open a dialog. - Introduced some additional separators (e.g. in "Dialogs"). - Reorder some plugins and the color correct tools after initialisation. - A menu entry to invoke the tooltips inspector. - A debugging menu entry which dumps the menu paths and their help pages (will of course go away when the help sys is consistent). There are currently two identical "Help" menus because <Toolbox>/Help trashes the menu bar if the toolbox is too narrow (gtk doesn't seem to support multi-line menubars, any idea?) * app/app_procs.c: call menus_reorder_plugins() after loading the plugins to beautify the "Xtns" menu. * app/commands.[ch]: reordered some functions to match the new menu structure (for easier source navigation) and renamed some to be consistent (e.g. all help functions are now called help_*). Show/Hide the rulers with ordinary gtk_widget_[show|hide]() commands. I've tested it several times and it looks exactly the same as the old code which used internal gtk knowledge. * app/gdisplay.c: applied the menu changes to gdisplay_set_menu_sensitivity(). * app/gimphelp.[ch]: new public function gimp_context_help() which invokes the tooltips inspector. Code cleanup. * app/resize.c: changed the dialogs' titles to match the menu entries. * app/session.c: renamed the gradient selection cmd callback to be consistent with brushes/patterns. * app/tools.c: added "..." to the menu paths of the tools which have dialogs. * app/fileops.c * app/channels_dialog.c * app/layers_dialog.c * app/paths_dialog.c: added some "...". * plug-ins/common/align_layers.c * plug-ins/common/autostretch_hsv.c * plug-ins/common/c_astretch.c * plug-ins/common/color_enhance.c * plug-ins/common/compose.c * plug-ins/common/decompose.c * plug-ins/common/mail.c * plug-ins/common/normalize.c * plug-ins/common/threshold_alpha.c * plug-ins/dbbrowser/dbbrowser.c * plug-ins/fp/fp.c * plug-ins/print/print.c * plug-ins/rcm/rcm.c: changed the menu paths and added "...".
1106 lines
32 KiB
C
1106 lines
32 KiB
C
/* 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 <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <ctype.h>
|
|
#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>/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;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|