gimp/app/color_picker.c
Tor Lillqvist f6858e21d1 Actually use the enum types GimpImageType, GimpImageBaseType,
* app/*.[ch]: Actually use the enum types GimpImageType,
	GimpImageBaseType, LayerModeEffects, PaintApplicationMode,
	BrushApplicationMode, GimpFillType and ConvertPaletteType, instead
	of just int or gint. Hopefully I catched most of the places
	where these should be used.

	Add an enum ConvolutionType, suffix the too general constants
	NORMAL, ABSOLUTE and NEGATIVE with _CONVOL. Use NORMAL_MODE
	instead of NORMAL in some places (this was what was intended). Fix
	some minor gccisms.

	* app/apptypes.h: New file. This file contains the above
	enumeration types, and some opaque struct typedefs. It was
	necessary to collect these in one header that doesn't include
	other headers, because when we started using the above mentioned
	types in the headers, all hell broke loose because of the
	spaghetti-like cross-inclusion mess between headers.

	(An example: Header A includes header B, which includes header C
	which includes A. B uses a type defined in A. This is not defined,
	because A hasn't defined it yet at the point where it includes B,
	and A included from B of course is skipped as we already are
	reading A.)
1999-08-18 23:41:39 +00:00

665 lines
20 KiB
C

/* The GIMP -- an image manipulation program
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* 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.
*/
#include "appenv.h"
#include "actionarea.h"
#include "color_picker.h"
#include "draw_core.h"
#include "drawable.h"
#include "gdisplay.h"
#include "cursorutil.h"
#include "info_dialog.h"
#include "palette.h"
#include "tool_options_ui.h"
#include "tools.h"
#include "gimprc.h"
#include "libgimp/gimpintl.h"
/* maximum information buffer size */
#define MAX_INFO_BUF 8
/* the color picker structures */
typedef struct _ColorPickerOptions ColorPickerOptions;
struct _ColorPickerOptions
{
ToolOptions tool_options;
gint sample_merged;
gint sample_merged_d;
GtkWidget *sample_merged_w;
gint sample_average;
gint sample_average_d;
GtkWidget *sample_average_w;
gdouble average_radius;
gdouble average_radius_d;
GtkObject *average_radius_w;
};
typedef struct _ColorPickerTool ColorPickerTool;
struct _ColorPickerTool
{
DrawCore *core; /* Core select object */
gint centerx; /* starting x coord */
gint centery; /* starting y coord */
};
/* the color picker tool options */
static ColorPickerOptions * color_picker_options = NULL;
/* the color value */
gint col_value[5] = { 0, 0, 0, 0, 0 };
/* the color picker dialog */
static gint update_type;
static GimpImageType sample_type;
static InfoDialog * color_picker_info = NULL;
static gchar red_buf [MAX_INFO_BUF];
static gchar green_buf [MAX_INFO_BUF];
static gchar blue_buf [MAX_INFO_BUF];
static gchar alpha_buf [MAX_INFO_BUF];
static gchar index_buf [MAX_INFO_BUF];
static gchar gray_buf [MAX_INFO_BUF];
static gchar hex_buf [MAX_INFO_BUF];
/* local function prototypes */
static void color_picker_button_press (Tool *, GdkEventButton *, gpointer);
static void color_picker_button_release (Tool *, GdkEventButton *, gpointer);
static void color_picker_motion (Tool *, GdkEventMotion *, gpointer);
static void color_picker_cursor_update (Tool *, GdkEventMotion *, gpointer);
static void color_picker_control (Tool *, ToolAction, gpointer);
static void color_picker_info_window_close_callback (GtkWidget *, gpointer);
static void color_picker_info_update (Tool *, gboolean);
/* functions */
static void
color_picker_options_reset (void)
{
ColorPickerOptions *options = color_picker_options;
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (options->sample_merged_w),
options->sample_merged_d);
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (options->sample_average_w),
options->sample_average_d);
gtk_adjustment_set_value (GTK_ADJUSTMENT (options->average_radius_w),
options->average_radius_d);
}
static ColorPickerOptions *
color_picker_options_new (void)
{
ColorPickerOptions *options;
GtkWidget *vbox;
GtkWidget *abox;
GtkWidget *table;
GtkWidget *label;
GtkWidget *scale;
/* the new color picker tool options structure */
options = g_new (ColorPickerOptions, 1);
tool_options_init ((ToolOptions *) options,
_("Color Picker Options"),
color_picker_options_reset);
options->sample_merged = options->sample_merged_d = FALSE;
options->sample_average = options->sample_average_d = FALSE;
options->average_radius = options->average_radius_d = 1.0;
/* the main vbox */
vbox = options->tool_options.main_vbox;
/* the sample merged toggle button */
options->sample_merged_w =
gtk_check_button_new_with_label (_("Sample Merged"));
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (options->sample_merged_w),
options->sample_merged_d);
gtk_box_pack_start (GTK_BOX (vbox), options->sample_merged_w, FALSE, FALSE, 0);
gtk_signal_connect (GTK_OBJECT (options->sample_merged_w), "toggled",
(GtkSignalFunc) tool_options_toggle_update,
&options->sample_merged);
gtk_widget_show (options->sample_merged_w);
/* the sample average options */
table = gtk_table_new (2, 2, FALSE);
gtk_table_set_col_spacing (GTK_TABLE (table), 0, 4);
gtk_box_pack_start (GTK_BOX (vbox), table, FALSE, FALSE, 0);
options->sample_average_w =
gtk_check_button_new_with_label (_("Sample Average"));
gtk_table_attach (GTK_TABLE (table), options->sample_average_w, 0, 1, 0, 1,
GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0);
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (options->sample_average_w),
options->sample_average_d);
gtk_signal_connect (GTK_OBJECT (options->sample_average_w), "toggled",
(GtkSignalFunc) tool_options_toggle_update,
&options->sample_average);
gtk_widget_show (options->sample_average_w);
label = gtk_label_new (_("Radius:"));
gtk_misc_set_alignment (GTK_MISC (label), 1.0, 1.0);
gtk_table_attach (GTK_TABLE (table), label, 0, 1, 1, 2,
GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0);
gtk_widget_show (label);
/* the feather radius scale */
abox = gtk_alignment_new (0.5, 1.0, 1.0, 0.0);
gtk_table_attach (GTK_TABLE (table), abox, 1, 2, 0, 2,
GTK_EXPAND | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0);
gtk_widget_show (abox);
options->average_radius_w =
gtk_adjustment_new (options->average_radius_d, 1.0, 15.0, 2.0, 2.0, 0.0);
scale = gtk_hscale_new (GTK_ADJUSTMENT (options->average_radius_w));
gtk_scale_set_digits (GTK_SCALE (scale), 0);
gtk_container_add (GTK_CONTAINER (abox), scale);
gtk_widget_set_sensitive (scale, options->sample_average_d);
gtk_object_set_data (GTK_OBJECT (options->sample_average_w), "set_sensitive",
scale);
gtk_widget_set_sensitive (label, options->sample_average_d);
gtk_object_set_data (GTK_OBJECT (scale), "set_sensitive",
label);
gtk_scale_set_value_pos (GTK_SCALE (scale), GTK_POS_TOP);
gtk_range_set_update_policy (GTK_RANGE (scale), GTK_UPDATE_DELAYED);
gtk_signal_connect (GTK_OBJECT (options->average_radius_w), "value_changed",
(GtkSignalFunc) tool_options_double_adjustment_update,
&options->average_radius);
gtk_widget_show (scale);
gtk_widget_show (table);
return options;
}
static void
color_picker_button_press (Tool *tool,
GdkEventButton *bevent,
gpointer gdisp_ptr)
{
GDisplay * gdisp;
ColorPickerTool *cp_tool;
gint x, y;
static ActionAreaItem action_items[] =
{
{ N_("Close"), color_picker_info_window_close_callback, NULL, NULL },
};
gdisp = (GDisplay *) gdisp_ptr;
cp_tool = (ColorPickerTool *) tool->private;
/* Make the tool active and set it's gdisplay & drawable */
tool->gdisp_ptr = gdisp;
tool->drawable = gimage_active_drawable (gdisp->gimage);
tool->state = ACTIVE;
/* create the info dialog if it doesn't exist */
if (! color_picker_info)
{
color_picker_info = info_dialog_new (_("Color Picker"));
/* if the gdisplay is for a color image, the dialog must have RGB */
switch (drawable_type (tool->drawable))
{
case RGB_GIMAGE: case RGBA_GIMAGE:
info_dialog_add_label (color_picker_info, _("Red:"), red_buf);
info_dialog_add_label (color_picker_info, _("Green:"), green_buf);
info_dialog_add_label (color_picker_info, _("Blue:"), blue_buf);
info_dialog_add_label (color_picker_info, _("Alpha:"), alpha_buf);
info_dialog_add_label (color_picker_info, _("Hex Triplet:"), hex_buf);
break;
case INDEXED_GIMAGE: case INDEXEDA_GIMAGE:
info_dialog_add_label (color_picker_info, _("Index:"), index_buf);
info_dialog_add_label (color_picker_info, _("Alpha:"), alpha_buf);
info_dialog_add_label (color_picker_info, _("Red:"), red_buf);
info_dialog_add_label (color_picker_info, _("Green:"), green_buf);
info_dialog_add_label (color_picker_info, _("Blue:"), blue_buf);
info_dialog_add_label (color_picker_info, _("Hex Triplet"), hex_buf);
break;
case GRAY_GIMAGE: case GRAYA_GIMAGE:
info_dialog_add_label (color_picker_info, _("Intensity:"), gray_buf);
info_dialog_add_label (color_picker_info, _("Alpha:"), alpha_buf);
info_dialog_add_label (color_picker_info, _("Hex Triplet:"), hex_buf);
break;
default :
break;
}
/* create the action area */
action_items[0].user_data = color_picker_info;
build_action_area (GTK_DIALOG (color_picker_info->shell),
action_items, 1, 0);
}
/* Keep the coordinates of the target */
gdisplay_untransform_coords (gdisp, bevent->x, bevent->y,
&cp_tool->centerx, &cp_tool->centery, FALSE, 1);
gdk_pointer_grab (gdisp->canvas->window, FALSE,
(GDK_POINTER_MOTION_HINT_MASK |
GDK_BUTTON1_MOTION_MASK |
GDK_BUTTON_RELEASE_MASK),
NULL, NULL, bevent->time);
/* First, transform the coordinates to gimp image space */
gdisplay_untransform_coords (gdisp, bevent->x, bevent->y, &x, &y,
FALSE, FALSE);
/* if the shift key is down, create a new color.
* otherwise, modify the current color.
*/
if (bevent->state & GDK_SHIFT_MASK)
{
color_picker_info_update (tool,
pick_color (gdisp->gimage, tool->drawable, x, y,
color_picker_options->sample_merged,
color_picker_options->sample_average,
color_picker_options->average_radius,
COLOR_NEW));
update_type = COLOR_UPDATE_NEW;
}
else
{
color_picker_info_update (tool,
pick_color (gdisp->gimage, tool->drawable, x, y,
color_picker_options->sample_merged,
color_picker_options->sample_average,
color_picker_options->average_radius,
COLOR_UPDATE));
update_type = COLOR_UPDATE;
}
/* Start drawing the colorpicker tool */
draw_core_start (cp_tool->core, gdisp->canvas->window, tool);
}
static void
color_picker_button_release (Tool *tool,
GdkEventButton *bevent,
gpointer gdisp_ptr)
{
GDisplay *gdisp;
ColorPickerTool *cp_tool;
gint x, y;
gdk_pointer_ungrab (bevent->time);
gdk_flush ();
gdisp = (GDisplay *) gdisp_ptr;
cp_tool = (ColorPickerTool *) tool->private;
/* First, transform the coordinates to gimp image space */
gdisplay_untransform_coords (gdisp, bevent->x, bevent->y, &x, &y,
FALSE, FALSE);
color_picker_info_update (tool,
pick_color (gdisp->gimage, tool->drawable, x, y,
color_picker_options->sample_merged,
color_picker_options->sample_average,
color_picker_options->average_radius,
update_type));
draw_core_stop (cp_tool->core, tool);
tool->state = INACTIVE;
}
static void
color_picker_motion (Tool *tool,
GdkEventMotion *mevent,
gpointer gdisp_ptr)
{
GDisplay *gdisp;
ColorPickerTool *cp_tool;
int x, y;
gdisp = (GDisplay *) gdisp_ptr;
cp_tool = (ColorPickerTool *) tool->private;
/* undraw the current tool */
draw_core_pause (cp_tool->core, tool);
/* First, transform the coordinates to gimp image space */
gdisplay_untransform_coords (gdisp, mevent->x, mevent->y, &x, &y,
FALSE, FALSE);
gdisplay_untransform_coords (gdisp, mevent->x, mevent->y,
&cp_tool->centerx, &cp_tool->centery,
FALSE, TRUE);
color_picker_info_update (tool,
pick_color (gdisp->gimage, tool->drawable, x, y,
color_picker_options->sample_merged,
color_picker_options->sample_average,
color_picker_options->average_radius,
update_type));
/* redraw the current tool */
draw_core_resume (cp_tool->core, tool);
}
static void
color_picker_cursor_update (Tool *tool,
GdkEventMotion *mevent,
gpointer gdisp_ptr)
{
GDisplay *gdisp;
int x, y;
gdisp = (GDisplay *) gdisp_ptr;
gdisplay_untransform_coords (gdisp, mevent->x, mevent->y, &x, &y,
FALSE, FALSE);
if (gimage_pick_correlate_layer (gdisp->gimage, x, y))
gdisplay_install_tool_cursor (gdisp, GIMP_COLOR_PICKER_CURSOR);
else
gdisplay_install_tool_cursor (gdisp, GDK_TOP_LEFT_ARROW);
}
static void
color_picker_control (Tool *tool,
ToolAction action,
gpointer gdisp_ptr)
{
ColorPickerTool * cp_tool;
cp_tool = (ColorPickerTool *) tool->private;
switch (action)
{
case PAUSE :
draw_core_pause (cp_tool->core, tool);
break;
case RESUME :
draw_core_resume (cp_tool->core, tool);
break;
case HALT :
draw_core_stop (cp_tool->core, tool);
break;
default:
break;
}
}
typedef guchar * (*GetColorFunc) (GtkObject *, int, int);
gboolean
pick_color (GimpImage *gimage,
GimpDrawable *drawable,
gint x,
gint y,
gboolean sample_merged,
gboolean sample_average,
gdouble average_radius,
gint final)
{
guchar *color;
gint offx, offy;
gint has_alpha;
gint is_indexed;
GetColorFunc get_color_func;
GtkObject *get_color_obj;
if (!drawable && !sample_merged)
return FALSE;
if (!sample_merged)
{
drawable_offsets (drawable, &offx, &offy);
x -= offx;
y -= offy;
sample_type = gimp_drawable_type (drawable);
is_indexed = gimp_drawable_indexed (drawable);
get_color_func = (GetColorFunc) gimp_drawable_get_color_at;
get_color_obj = GTK_OBJECT (drawable);
}
else
{
sample_type = gimp_image_composite_type (gimage);
is_indexed = FALSE;
get_color_func = (GetColorFunc) gimp_image_get_color_at;
get_color_obj = GTK_OBJECT (gimage);
}
has_alpha = TYPE_HAS_ALPHA (sample_type);
if (!(color = (*get_color_func) (get_color_obj, x, y)))
return FALSE;
if (sample_average)
{
gint i, j;
gint count = 0;
gint color_avg[4] = { 0, 0, 0, 0 };
guchar *tmp_color;
gint radius = (gint) average_radius;
for (i = x - radius; i <= x + radius; i++)
for (j = y - radius; j <= y + radius; j++)
if ((tmp_color = (*get_color_func) (get_color_obj, i, j)))
{
count++;
color_avg[RED_PIX] += tmp_color[RED_PIX];
color_avg[GREEN_PIX] += tmp_color[GREEN_PIX];
color_avg[BLUE_PIX] += tmp_color[BLUE_PIX];
if (has_alpha)
color_avg[ALPHA_PIX] += tmp_color[3];
g_free(tmp_color);
}
color[RED_PIX] = (guchar) (color_avg[RED_PIX] / count);
color[GREEN_PIX] = (guchar) (color_avg[GREEN_PIX] / count);
color[BLUE_PIX] = (guchar) (color_avg[BLUE_PIX] / count);
if (has_alpha)
color[ALPHA_PIX] = (guchar) (color_avg[3] / count);
is_indexed = FALSE;
}
col_value[RED_PIX] = color[RED_PIX];
col_value[GREEN_PIX] = color[GREEN_PIX];
col_value[BLUE_PIX] = color[BLUE_PIX];
if (has_alpha)
col_value[ALPHA_PIX] = color[3];
if (is_indexed)
col_value[4] = color[4];
palette_set_active_color (col_value [RED_PIX], col_value [GREEN_PIX],
col_value [BLUE_PIX], final);
g_free (color);
return TRUE;
}
static void
colorpicker_draw (Tool *tool)
{
GDisplay * gdisp;
ColorPickerTool * cp_tool;
gint tx, ty;
gint radiusx, radiusy;
gint cx, cy;
if(!color_picker_options->sample_average)
return;
gdisp = (GDisplay *) tool->gdisp_ptr;
cp_tool = (ColorPickerTool *) tool->private;
gdisplay_transform_coords (gdisp, cp_tool->centerx, cp_tool->centery,
&tx, &ty, TRUE);
radiusx = SCALEX (gdisp, color_picker_options->average_radius);
radiusy = SCALEY (gdisp, color_picker_options->average_radius);
cx = SCALEX (gdisp, 1);
cy = SCALEY (gdisp, 1);
/* Draw the circle around the collecting area */
gdk_draw_rectangle (cp_tool->core->win, cp_tool->core->gc, 0,
tx - radiusx,
ty - radiusy,
2 * radiusx + cx, 2 * radiusy + cy);
if(radiusx > 1 && radiusy > 1)
{
gdk_draw_rectangle (cp_tool->core->win, cp_tool->core->gc, 0,
tx - radiusx + 2,
ty - radiusy + 2,
2 * radiusx + cx - 4, 2 * radiusy + cy - 4);
}
}
static void
color_picker_info_update (Tool *tool,
gboolean valid)
{
if (!valid)
{
g_snprintf (red_buf, MAX_INFO_BUF, _("N/A"));
g_snprintf (green_buf, MAX_INFO_BUF, _("N/A"));
g_snprintf (blue_buf, MAX_INFO_BUF, _("N/A"));
g_snprintf (alpha_buf, MAX_INFO_BUF, _("N/A"));
g_snprintf (index_buf, MAX_INFO_BUF, _("N/A"));
g_snprintf (gray_buf, MAX_INFO_BUF, _("N/A"));
g_snprintf (hex_buf, MAX_INFO_BUF, _("N/A"));
}
else
{
switch (sample_type)
{
case RGB_GIMAGE: case RGBA_GIMAGE:
g_snprintf (red_buf, MAX_INFO_BUF, "%d", col_value [RED_PIX]);
g_snprintf (green_buf, MAX_INFO_BUF, "%d", col_value [GREEN_PIX]);
g_snprintf (blue_buf, MAX_INFO_BUF, "%d", col_value [BLUE_PIX]);
if (sample_type == RGBA_GIMAGE)
g_snprintf (alpha_buf, MAX_INFO_BUF, "%d", col_value [ALPHA_PIX]);
else
g_snprintf (alpha_buf, MAX_INFO_BUF, _("N/A"));
g_snprintf (hex_buf, MAX_INFO_BUF, "#%.2x%.2x%.2x",
col_value [RED_PIX],
col_value [GREEN_PIX],
col_value [BLUE_PIX]);
break;
case INDEXED_GIMAGE: case INDEXEDA_GIMAGE:
g_snprintf (index_buf, MAX_INFO_BUF, "%d", col_value [4]);
if (sample_type == INDEXEDA_GIMAGE)
g_snprintf (alpha_buf, MAX_INFO_BUF, "%d", col_value [ALPHA_PIX]);
else
g_snprintf (alpha_buf, MAX_INFO_BUF, _("N/A"));
g_snprintf (red_buf, MAX_INFO_BUF, "%d", col_value [RED_PIX]);
g_snprintf (green_buf, MAX_INFO_BUF, "%d", col_value [GREEN_PIX]);
g_snprintf (blue_buf, MAX_INFO_BUF, "%d", col_value [BLUE_PIX]);
g_snprintf (hex_buf, MAX_INFO_BUF, "#%.2x%.2x%.2x",
col_value [RED_PIX],
col_value [GREEN_PIX],
col_value [BLUE_PIX]);
break;
case GRAY_GIMAGE: case GRAYA_GIMAGE:
g_snprintf (gray_buf, MAX_INFO_BUF, "%d", col_value [GRAY_PIX]);
if (sample_type == GRAYA_GIMAGE)
g_snprintf (alpha_buf, MAX_INFO_BUF, "%d", col_value [ALPHA_PIX]);
else
g_snprintf (alpha_buf, MAX_INFO_BUF, _("N/A"));
g_snprintf (hex_buf, MAX_INFO_BUF, "#%.2x%.2x%.2x",
col_value [GRAY_PIX],
col_value [GRAY_PIX],
col_value [GRAY_PIX]);
break;
}
}
info_dialog_update (color_picker_info);
info_dialog_popup (color_picker_info);
}
static void
color_picker_info_window_close_callback (GtkWidget *widget,
gpointer client_data)
{
info_dialog_popdown ((InfoDialog *) client_data);
}
Tool *
tools_new_color_picker ()
{
Tool * tool;
ColorPickerTool * private;
/* The tool options */
if (! color_picker_options)
{
color_picker_options = color_picker_options_new ();
tools_register (COLOR_PICKER, (ToolOptions *) color_picker_options);
}
tool = tools_new_tool (COLOR_PICKER);
private = g_new (ColorPickerTool, 1);
private->core = draw_core_new (colorpicker_draw);
tool->preserve = FALSE; /* Don't preserve on drawable change */
tool->private = (void *) private;
tool->button_press_func = color_picker_button_press;
tool->button_release_func = color_picker_button_release;
tool->motion_func = color_picker_motion;
tool->cursor_update_func = color_picker_cursor_update;
tool->control_func = color_picker_control;
return tool;
}
void
tools_free_color_picker (Tool *tool)
{
ColorPickerTool * cp_tool;
cp_tool = (ColorPickerTool *) tool->private;
if (tool->state == ACTIVE)
draw_core_stop (cp_tool->core, tool);
draw_core_free (cp_tool->core);
if (color_picker_info)
{
info_dialog_free (color_picker_info);
color_picker_info = NULL;
}
g_free (cp_tool);
}