gimp/app/tools/gimpcurvestool.c
Michael Natterer 6eb772946b libgimpwidgets/gimpquerybox.c configure the labels in the message dialog
2003-11-14  Michael Natterer  <mitch@gimp.org>

	* libgimpwidgets/gimpquerybox.c
	* app/widgets/gimpwidgets-utils.c: configure the labels in the
	message dialog and the query boxes to do automatic word wrapping
	to be HIG compliant.

	* app/app_procs.c
	* app/batch.c
	* app/config/gimpconfig-deserialize.c
	* app/config/gimpconfig-path.c
	* app/config/gimpconfig-utils.c
	* app/config/gimpconfigwriter.c
	* app/config/gimpscanner.c
	* app/core/gimpbrush.c
	* app/core/gimpbrushgenerated.c
	* app/core/gimpbrushpipe.c
	* app/core/gimpdatafactory.c
	* app/core/gimpgradient.c
	* app/core/gimpimage-merge.c
	* app/core/gimpimage.c
	* app/core/gimpimagefile.c
	* app/core/gimplayer-floating-sel.c
	* app/core/gimppalette.c
	* app/core/gimppattern.c
	* app/core/gimpselection.c
	* app/display/gimpdisplayshell.c
	* app/file/file-utils.c
	* app/gui/brush-select.c
	* app/gui/dialogs-commands.c
	* app/gui/drawable-commands.c
	* app/gui/edit-commands.c
	* app/gui/file-commands.c
	* app/gui/file-new-dialog.c
	* app/gui/font-select.c
	* app/gui/gradient-select.c
	* app/gui/gui.c
	* app/gui/image-commands.c
	* app/gui/layers-commands.c
	* app/gui/palette-select.c
	* app/gui/palettes-commands.c
	* app/gui/pattern-select.c
	* app/gui/preferences-dialog.c
	* app/gui/select-commands.c
	* app/gui/stroke-dialog.c
	* app/gui/tool-options-menu.c
	* app/gui/vectors-commands.c
	* app/gui/view-commands.c
	* app/plug-in/plug-in-message.c
	* app/plug-in/plug-in.c
	* app/plug-in/plug-ins.c
	* app/text/gimptextlayer-xcf.c
	* app/text/gimptextlayer.c
	* app/tools/gimpcurvestool.c
	* app/tools/gimphuesaturationtool.c
	* app/tools/gimplevelstool.c
	* app/tools/gimptransformtool.c
	* app/vectors/gimpvectors-export.c
	* app/widgets/gimpdatafactoryview.c
	* app/widgets/gimphelp.c
	* app/widgets/gimptemplateview.c
	* app/widgets/gimptooloptionseditor.c
	* app/xcf/xcf.c
	* tools/pdbgen/pdb/image.pdb: removed explicit newlines from
	messages. Reduced number of translatable strings by making many
	file error messages the same. Quote single words and filenames
	with 'foo', not "foo". Replaced some more "drawable" by "layer".
	General message cleanup and consistency check.

	* app/pdb/image_cmds.c: regenerated.
2003-11-14 15:33:40 +00:00

1385 lines
39 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 "config.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#ifdef __GNUC__
#warning GTK_DISABLE_DEPRECATED
#endif
#undef GTK_DISABLE_DEPRECATED
#include <gtk/gtk.h>
#include "libgimpmath/gimpmath.h"
#include "libgimpbase/gimpbase.h"
#include "libgimpcolor/gimpcolor.h"
#include "libgimpwidgets/gimpwidgets.h"
#include "tools-types.h"
#include "base/curves.h"
#include "base/gimphistogram.h"
#include "base/gimplut.h"
#include "core/gimpdrawable.h"
#include "core/gimpimage.h"
#include "core/gimpimagemap.h"
#include "core/gimptoolinfo.h"
#include "widgets/gimpcursor.h"
#include "widgets/gimpenummenu.h"
#include "widgets/gimphelp-ids.h"
#include "display/gimpdisplay.h"
#include "gimpcoloroptions.h"
#include "gimpcurvestool.h"
#include "gimptoolcontrol.h"
#include "gimp-intl.h"
#define XRANGE_TOP (1 << 0)
#define XRANGE_BOTTOM (1 << 1)
#define YRANGE (1 << 2)
#define ALL (XRANGE_TOP | XRANGE_BOTTOM | YRANGE)
/* NB: take care when changing these values: make sure the curve[] array in
* base/curves.h is large enough.
*/
#define GRAPH_WIDTH 256
#define GRAPH_HEIGHT 256
#define XRANGE_WIDTH 256
#define XRANGE_HEIGHT 12
#define YRANGE_WIDTH 12
#define YRANGE_HEIGHT 256
#define RADIUS 3
#define MIN_DISTANCE 8
#define GRAPH_MASK (GDK_EXPOSURE_MASK | \
GDK_LEAVE_NOTIFY_MASK | \
GDK_POINTER_MOTION_MASK | \
GDK_BUTTON_PRESS_MASK | \
GDK_BUTTON_RELEASE_MASK)
/* local function prototypes */
static void gimp_curves_tool_class_init (GimpCurvesToolClass *klass);
static void gimp_curves_tool_init (GimpCurvesTool *c_tool);
static void gimp_curves_tool_finalize (GObject *object);
static void gimp_curves_tool_initialize (GimpTool *tool,
GimpDisplay *gdisp);
static void gimp_curves_tool_button_release (GimpTool *tool,
GimpCoords *coords,
guint32 time,
GdkModifierType state,
GimpDisplay *gdisp);
static void gimp_curves_tool_color_picked (GimpColorTool *color_tool,
GimpImageType sample_type,
GimpRGB *color,
gint color_index);
static void gimp_curves_tool_map (GimpImageMapTool *image_map_tool);
static void gimp_curves_tool_dialog (GimpImageMapTool *image_map_tool);
static void gimp_curves_tool_reset (GimpImageMapTool *image_map_tool);
static void curves_add_point (GimpCurvesTool *c_tool,
gint x,
gint y,
gint cchan);
static void curves_update (GimpCurvesTool *c_tool,
gint update);
static void curves_channel_callback (GtkWidget *widget,
GimpCurvesTool *c_tool);
static void curves_channel_reset_callback (GtkWidget *widget,
GimpCurvesTool *c_tool);
static gboolean curves_set_sensitive_callback (gpointer item_data,
GimpCurvesTool *c_tool);
static void curves_curve_type_callback (GtkWidget *widget,
GimpCurvesTool *c_tool);
static void curves_load_callback (GtkWidget *widget,
GimpCurvesTool *c_tool);
static void curves_save_callback (GtkWidget *widget,
GimpCurvesTool *c_tool);
static gint curves_graph_events (GtkWidget *widget,
GdkEvent *event,
GimpCurvesTool *c_tool);
static void curves_graph_expose (GtkWidget *widget,
GdkRectangle *area,
GimpCurvesTool *c_tool);
static void file_dialog_create (GimpCurvesTool *c_tool);
static void file_dialog_ok_callback (GimpCurvesTool *c_tool);
static gboolean curves_read_from_file (GimpCurvesTool *c_tool,
FILE *file);
static void curves_write_to_file (GimpCurvesTool *c_tool,
FILE *file);
static GimpImageMapToolClass *parent_class = NULL;
/* public functions */
void
gimp_curves_tool_register (GimpToolRegisterCallback callback,
gpointer data)
{
(* callback) (GIMP_TYPE_CURVES_TOOL,
GIMP_TYPE_COLOR_OPTIONS,
gimp_color_options_gui,
0,
"gimp-curves-tool",
_("Curves"),
_("Adjust color curves"),
N_("/Tools/Color Tools/_Curves..."), NULL,
NULL, GIMP_HELP_TOOL_CURVES,
GIMP_STOCK_TOOL_CURVES,
data);
}
GType
gimp_curves_tool_get_type (void)
{
static GType tool_type = 0;
if (! tool_type)
{
static const GTypeInfo tool_info =
{
sizeof (GimpCurvesToolClass),
(GBaseInitFunc) NULL,
(GBaseFinalizeFunc) NULL,
(GClassInitFunc) gimp_curves_tool_class_init,
NULL, /* class_finalize */
NULL, /* class_data */
sizeof (GimpCurvesTool),
0, /* n_preallocs */
(GInstanceInitFunc) gimp_curves_tool_init,
};
tool_type = g_type_register_static (GIMP_TYPE_IMAGE_MAP_TOOL,
"GimpCurvesTool",
&tool_info, 0);
}
return tool_type;
}
/* private functions */
static void
gimp_curves_tool_class_init (GimpCurvesToolClass *klass)
{
GObjectClass *object_class;
GimpToolClass *tool_class;
GimpColorToolClass *color_tool_class;
GimpImageMapToolClass *image_map_tool_class;
object_class = G_OBJECT_CLASS (klass);
tool_class = GIMP_TOOL_CLASS (klass);
color_tool_class = GIMP_COLOR_TOOL_CLASS (klass);
image_map_tool_class = GIMP_IMAGE_MAP_TOOL_CLASS (klass);
parent_class = g_type_class_peek_parent (klass);
object_class->finalize = gimp_curves_tool_finalize;
tool_class->initialize = gimp_curves_tool_initialize;
tool_class->button_release = gimp_curves_tool_button_release;
color_tool_class->picked = gimp_curves_tool_color_picked;
image_map_tool_class->map = gimp_curves_tool_map;
image_map_tool_class->dialog = gimp_curves_tool_dialog;
image_map_tool_class->reset = gimp_curves_tool_reset;
}
static void
gimp_curves_tool_init (GimpCurvesTool *c_tool)
{
GimpImageMapTool *image_map_tool = GIMP_IMAGE_MAP_TOOL (c_tool);
gint i;
image_map_tool->shell_desc = _("Adjust Color Curves");
c_tool->curves = g_new0 (Curves, 1);
c_tool->lut = gimp_lut_new ();
c_tool->channel = GIMP_HISTOGRAM_VALUE;
curves_init (c_tool->curves);
for (i = 0; i < G_N_ELEMENTS (c_tool->col_value); i++)
c_tool->col_value[i] = -1;
c_tool->cursor_x = -1;
c_tool->cursor_y = -1;
}
static void
gimp_curves_tool_finalize (GObject *object)
{
GimpCurvesTool *c_tool = GIMP_CURVES_TOOL (object);
if (c_tool->curves)
{
g_free (c_tool->curves);
c_tool->curves = NULL;
}
if (c_tool->lut)
{
gimp_lut_free (c_tool->lut);
c_tool->lut = NULL;
}
if (c_tool->cursor_layout)
{
g_object_unref (c_tool->cursor_layout);
c_tool->cursor_layout = NULL;
}
if (c_tool->xpos_layout)
{
g_object_unref (c_tool->xpos_layout);
c_tool->xpos_layout = NULL;
}
G_OBJECT_CLASS (parent_class)->finalize (object);
}
static void
gimp_curves_tool_initialize (GimpTool *tool,
GimpDisplay *gdisp)
{
GimpCurvesTool *c_tool = GIMP_CURVES_TOOL (tool);
GimpDrawable *drawable;
drawable = gimp_image_active_drawable (gdisp->gimage);
if (! drawable)
return;
if (gimp_drawable_is_indexed (drawable))
{
g_message (_("Curves for indexed layers cannot be adjusted."));
return;
}
curves_init (c_tool->curves);
c_tool->color = gimp_drawable_is_rgb (drawable);
c_tool->channel = GIMP_HISTOGRAM_VALUE;
c_tool->grab_point = -1;
c_tool->last = 0;
GIMP_TOOL_CLASS (parent_class)->initialize (tool, gdisp);
/* always pick colors */
gimp_color_tool_enable (GIMP_COLOR_TOOL (tool),
GIMP_COLOR_OPTIONS (tool->tool_info->tool_options));
/* set the sensitivity of the channel menu based on the drawable type */
gimp_option_menu_set_sensitive (GTK_OPTION_MENU (c_tool->channel_menu),
(GimpOptionMenuSensitivityCallback) curves_set_sensitive_callback,
c_tool);
/* set the current selection */
gimp_option_menu_set_history (GTK_OPTION_MENU (c_tool->channel_menu),
GINT_TO_POINTER (c_tool->channel));
curves_update (c_tool, ALL);
}
static void
gimp_curves_tool_button_release (GimpTool *tool,
GimpCoords *coords,
guint32 time,
GdkModifierType state,
GimpDisplay *gdisp)
{
GimpCurvesTool *c_tool = GIMP_CURVES_TOOL (tool);
GimpDrawable *drawable;
drawable = gimp_image_active_drawable (gdisp->gimage);
if (state & GDK_SHIFT_MASK)
{
curves_add_point (c_tool, coords->x, coords->y, c_tool->channel);
curves_calculate_curve (c_tool->curves, c_tool->channel);
}
else if (state & GDK_CONTROL_MASK)
{
gint i;
for (i = 0; i < 5; i++)
{
curves_add_point (c_tool, coords->x, coords->y, i);
curves_calculate_curve (c_tool->curves, c_tool->channel);
}
}
/* chain up to halt the tool */
GIMP_TOOL_CLASS (parent_class)->button_release (tool,
coords, time, state, gdisp);
}
static void
gimp_curves_tool_color_picked (GimpColorTool *color_tool,
GimpImageType sample_type,
GimpRGB *color,
gint color_index)
{
GimpCurvesTool *c_tool;
GimpDrawable *drawable;
guchar r, g, b, a;
c_tool = GIMP_CURVES_TOOL (color_tool);
drawable = GIMP_IMAGE_MAP_TOOL (c_tool)->drawable;
gimp_rgba_get_uchar (color, &r, &g, &b, &a);
c_tool->col_value[GIMP_HISTOGRAM_RED] = r;
c_tool->col_value[GIMP_HISTOGRAM_GREEN] = g;
c_tool->col_value[GIMP_HISTOGRAM_BLUE] = b;
if (gimp_drawable_has_alpha (drawable))
c_tool->col_value[GIMP_HISTOGRAM_ALPHA] = a;
if (gimp_drawable_is_indexed (drawable))
c_tool->col_value[GIMP_HISTOGRAM_ALPHA] = color_index;
c_tool->col_value[GIMP_HISTOGRAM_VALUE] = MAX (MAX (r, g), b);
gtk_widget_queue_draw (c_tool->graph);
}
static void
curves_add_point (GimpCurvesTool *c_tool,
gint x,
gint y,
gint cchan)
{
/* Add point onto the curve */
gint closest_point = 0;
gint distance;
gint curvex;
gint i;
switch (c_tool->curves->curve_type[cchan])
{
case GIMP_CURVE_SMOOTH:
curvex = c_tool->col_value[cchan];
distance = G_MAXINT;
for (i = 0; i < 17; i++)
{
if (c_tool->curves->points[cchan][i][0] != -1)
if (abs (curvex - c_tool->curves->points[cchan][i][0]) < distance)
{
distance = abs (curvex - c_tool->curves->points[cchan][i][0]);
closest_point = i;
}
}
if (distance > MIN_DISTANCE)
closest_point = (curvex + 8) / 16;
c_tool->curves->points[cchan][closest_point][0] = curvex;
c_tool->curves->points[cchan][closest_point][1] = c_tool->curves->curve[cchan][curvex];
break;
case GIMP_CURVE_FREE:
c_tool->curves->curve[cchan][x] = 255 - y;
break;
}
}
static void
gimp_curves_tool_map (GimpImageMapTool *image_map_tool)
{
GimpCurvesTool *c_tool = GIMP_CURVES_TOOL (image_map_tool);
gimp_lut_setup (c_tool->lut,
(GimpLutFunc) curves_lut_func,
c_tool->curves,
gimp_drawable_bytes (image_map_tool->drawable));
gimp_image_map_apply (image_map_tool->image_map,
(GimpImageMapApplyFunc) gimp_lut_process_2,
c_tool->lut);
}
/*******************/
/* Curves dialog */
/*******************/
static void
gimp_curves_tool_dialog (GimpImageMapTool *image_map_tool)
{
GimpCurvesTool *c_tool = GIMP_CURVES_TOOL (image_map_tool);
GtkWidget *hbox;
GtkWidget *vbox;
GtkWidget *hbbox;
GtkWidget *frame;
GtkWidget *menu;
GtkWidget *table;
GtkWidget *button;
hbox = gtk_hbox_new (FALSE, 0);
gtk_box_pack_start (GTK_BOX (image_map_tool->main_vbox), hbox,
FALSE, FALSE, 0);
gtk_widget_show (hbox);
vbox = gtk_vbox_new (FALSE, 4);
gtk_box_pack_start (GTK_BOX (hbox), vbox, TRUE, FALSE, 0);
gtk_widget_show (vbox);
table = gtk_table_new (2, 2, FALSE);
gtk_table_set_col_spacings (GTK_TABLE (table), 4);
gtk_table_set_row_spacings (GTK_TABLE (table), 2);
gtk_box_pack_start (GTK_BOX (vbox), table, FALSE, FALSE, 0);
gtk_widget_show (table);
/* The option menu for selecting channels */
hbox = gtk_hbox_new (FALSE, 4);
menu = gimp_enum_option_menu_new (GIMP_TYPE_HISTOGRAM_CHANNEL,
G_CALLBACK (curves_channel_callback),
c_tool);
gimp_enum_option_menu_set_stock_prefix (GTK_OPTION_MENU (menu),
"gimp-channel");
gtk_box_pack_start (GTK_BOX (hbox), menu, FALSE, FALSE, 0);
gtk_widget_show (menu);
c_tool->channel_menu = menu;
button = gtk_button_new_with_mnemonic (_("R_eset Channel"));
gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
gtk_widget_show (button);
g_signal_connect (button, "clicked",
G_CALLBACK (curves_channel_reset_callback),
c_tool);
gimp_table_attach_aligned (GTK_TABLE (table), 0, 0,
_("Modify Curves for Channel:"), 1.0, 0.5,
hbox, 1, FALSE);
/* The table for the yrange and the graph */
hbox = gtk_hbox_new (FALSE, 0);
gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
gtk_widget_show (hbox);
table = gtk_table_new (2, 2, FALSE);
gtk_table_set_col_spacings (GTK_TABLE (table), 2);
gtk_table_set_row_spacings (GTK_TABLE (table), 2);
gtk_box_pack_start (GTK_BOX (hbox), table, TRUE, FALSE, 0);
/* The range drawing area */
frame = gtk_frame_new (NULL);
gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
gtk_table_attach (GTK_TABLE (table), frame, 0, 1, 0, 1,
GTK_EXPAND, GTK_EXPAND, 0, 0);
gtk_widget_show (frame);
c_tool->yrange = gtk_preview_new (GTK_PREVIEW_COLOR);
gtk_preview_size (GTK_PREVIEW (c_tool->yrange), YRANGE_WIDTH, YRANGE_HEIGHT);
gtk_container_add (GTK_CONTAINER (frame), c_tool->yrange);
gtk_widget_show (c_tool->yrange);
/* The curves graph */
frame = gtk_frame_new (NULL);
gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
gtk_table_attach (GTK_TABLE (table), frame, 1, 2, 0, 1,
GTK_SHRINK | GTK_FILL,
GTK_SHRINK | GTK_FILL, 0, 0);
gtk_widget_show (frame);
c_tool->graph = gtk_drawing_area_new ();
gtk_widget_set_size_request (c_tool->graph,
GRAPH_WIDTH + RADIUS * 2,
GRAPH_HEIGHT + RADIUS * 2);
gtk_widget_set_events (c_tool->graph, GRAPH_MASK);
gtk_container_add (GTK_CONTAINER (frame), c_tool->graph);
gtk_widget_show (c_tool->graph);
g_signal_connect (c_tool->graph, "event",
G_CALLBACK (curves_graph_events),
c_tool);
/* The range drawing area */
frame = gtk_frame_new (NULL);
gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
gtk_table_attach (GTK_TABLE (table), frame, 1, 2, 1, 2,
GTK_EXPAND, GTK_EXPAND, 0, 0);
gtk_widget_show (frame);
c_tool->xrange = gtk_preview_new (GTK_PREVIEW_COLOR);
gtk_preview_size (GTK_PREVIEW (c_tool->xrange), XRANGE_WIDTH, XRANGE_HEIGHT);
gtk_container_add (GTK_CONTAINER (frame), c_tool->xrange);
gtk_widget_show (c_tool->xrange);
gtk_widget_show (table);
hbox = gtk_hbox_new (FALSE, 6);
gtk_box_pack_end (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
gtk_widget_show (hbox);
/* Horizontal button box for load / save */
frame = gtk_frame_new (_("All Channels"));
gtk_box_pack_start (GTK_BOX (hbox), frame, FALSE, FALSE, 0);
gtk_widget_show (frame);
hbbox = gtk_hbutton_box_new ();
gtk_container_set_border_width (GTK_CONTAINER (hbbox), 2);
gtk_box_set_spacing (GTK_BOX (hbbox), 4);
gtk_button_box_set_layout (GTK_BUTTON_BOX (hbbox), GTK_BUTTONBOX_SPREAD);
gtk_container_add (GTK_CONTAINER (frame), hbbox);
gtk_widget_show (hbbox);
button = gtk_button_new_from_stock (GTK_STOCK_OPEN);
GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
gtk_box_pack_start (GTK_BOX (hbbox), button, FALSE, FALSE, 0);
gimp_help_set_help_data (button, _("Read curves settings from file"), NULL);
gtk_widget_show (button);
g_signal_connect (button, "clicked",
G_CALLBACK (curves_load_callback),
c_tool);
button = gtk_button_new_from_stock (GTK_STOCK_SAVE);
GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
gtk_box_pack_start (GTK_BOX (hbbox), button, FALSE, FALSE, 0);
gimp_help_set_help_data (button, _("Save curves settings to file"), NULL);
gtk_widget_show (button);
g_signal_connect (button, "clicked",
G_CALLBACK (curves_save_callback),
c_tool);
/* The radio box for selecting the curve type */
frame = gtk_frame_new (_("Curve Type"));
gtk_box_pack_end (GTK_BOX (hbox), frame, FALSE, FALSE, 0);
gtk_widget_show (frame);
hbox = gimp_enum_stock_box_new (GIMP_TYPE_CURVE_TYPE,
"gimp-curve", GTK_ICON_SIZE_MENU,
G_CALLBACK (curves_curve_type_callback),
c_tool,
&c_tool->curve_type);
gtk_container_set_border_width (GTK_CONTAINER (hbox), 2);
gtk_box_set_spacing (GTK_BOX (hbox), 4);
gtk_container_add (GTK_CONTAINER (frame), hbox);
gtk_widget_show (hbox);
}
static void
gimp_curves_tool_reset (GimpImageMapTool *image_map_tool)
{
GimpCurvesTool *c_tool = GIMP_CURVES_TOOL (image_map_tool);
GimpHistogramChannel channel;
c_tool->grab_point = -1;
for (channel = GIMP_HISTOGRAM_VALUE;
channel <= GIMP_HISTOGRAM_ALPHA;
channel++)
curves_channel_reset (c_tool->curves, channel);
curves_update (c_tool, XRANGE_TOP);
gtk_widget_queue_draw (c_tool->graph);
}
/* TODO: preview alpha channel stuff correctly. -- austin, 20/May/99 */
static void
curves_update (GimpCurvesTool *c_tool,
gint update)
{
GimpHistogramChannel sel_channel;
gint i, j;
if (c_tool->color)
{
sel_channel = c_tool->channel;
}
else
{
if (c_tool->channel == 2)
sel_channel = GIMP_HISTOGRAM_ALPHA;
else
sel_channel = GIMP_HISTOGRAM_VALUE;
}
if (update & XRANGE_TOP)
{
guchar buf[XRANGE_WIDTH * 3];
switch (sel_channel)
{
case GIMP_HISTOGRAM_VALUE:
case GIMP_HISTOGRAM_ALPHA:
for (i = 0; i < XRANGE_HEIGHT / 2; i++)
{
for (j = 0; j < XRANGE_WIDTH ; j++)
{
buf[j * 3 + 0] = c_tool->curves->curve[sel_channel][j];
buf[j * 3 + 1] = c_tool->curves->curve[sel_channel][j];
buf[j * 3 + 2] = c_tool->curves->curve[sel_channel][j];
}
gtk_preview_draw_row (GTK_PREVIEW (c_tool->xrange),
buf, 0, i, XRANGE_WIDTH);
}
break;
case GIMP_HISTOGRAM_RED:
case GIMP_HISTOGRAM_GREEN:
case GIMP_HISTOGRAM_BLUE:
{
for (i = 0; i < XRANGE_HEIGHT / 2; i++)
{
for (j = 0; j < XRANGE_WIDTH; j++)
{
buf[j * 3 + 0] = c_tool->curves->curve[GIMP_HISTOGRAM_RED][j];
buf[j * 3 + 1] = c_tool->curves->curve[GIMP_HISTOGRAM_GREEN][j];
buf[j * 3 + 2] = c_tool->curves->curve[GIMP_HISTOGRAM_BLUE][j];
}
gtk_preview_draw_row (GTK_PREVIEW (c_tool->xrange),
buf, 0, i, XRANGE_WIDTH);
}
break;
}
default:
g_warning ("unknown channel type %d, can't happen!?!?",
c_tool->channel);
break;
}
gtk_widget_queue_draw_area (c_tool->xrange,
0, 0,
XRANGE_WIDTH, XRANGE_HEIGHT / 2);
}
if (update & XRANGE_BOTTOM)
{
guchar buf[XRANGE_WIDTH * 3];
for (i = 0; i < XRANGE_WIDTH; i++)
{
buf[i * 3 + 0] = i;
buf[i * 3 + 1] = i;
buf[i * 3 + 2] = i;
}
for (i = XRANGE_HEIGHT / 2; i < XRANGE_HEIGHT; i++)
gtk_preview_draw_row (GTK_PREVIEW (c_tool->xrange),
buf, 0, i, XRANGE_WIDTH);
gtk_widget_queue_draw_area (c_tool->xrange,
0, XRANGE_HEIGHT / 2,
XRANGE_WIDTH, XRANGE_HEIGHT / 2);
}
if (update & YRANGE)
{
guchar buf[YRANGE_WIDTH * 3];
guchar pix[3];
for (i = 0; i < YRANGE_HEIGHT; i++)
{
switch (sel_channel)
{
case GIMP_HISTOGRAM_VALUE:
case GIMP_HISTOGRAM_ALPHA:
pix[0] = pix[1] = pix[2] = (255 - i);
break;
case GIMP_HISTOGRAM_RED:
case GIMP_HISTOGRAM_GREEN:
case GIMP_HISTOGRAM_BLUE:
pix[0] = pix[1] = pix[2] = 0;
pix[sel_channel - 1] = (255 - i);
break;
default:
g_warning ("unknown channel type %d, can't happen!?!?",
c_tool->channel);
break;
}
for (j = 0; j < YRANGE_WIDTH * 3; j++)
buf[j] = pix[j%3];
gtk_preview_draw_row (GTK_PREVIEW (c_tool->yrange),
buf, 0, i, YRANGE_WIDTH);
}
gtk_widget_queue_draw (c_tool->yrange);
}
}
static void
curves_channel_callback (GtkWidget *widget,
GimpCurvesTool *c_tool)
{
gimp_menu_item_update (widget, &c_tool->channel);
if (! c_tool->color)
{
if (c_tool->channel > 1)
c_tool->channel = 2;
else
c_tool->channel = 1;
}
gimp_radio_group_set_active (GTK_RADIO_BUTTON (c_tool->curve_type),
GINT_TO_POINTER (c_tool->curves->curve_type[c_tool->channel]));
curves_update (c_tool, XRANGE_TOP | YRANGE);
gtk_widget_queue_draw (c_tool->graph);
}
static void
curves_channel_reset_callback (GtkWidget *widget,
GimpCurvesTool *c_tool)
{
c_tool->grab_point = -1;
curves_channel_reset (c_tool->curves, c_tool->channel);
curves_update (c_tool, XRANGE_TOP);
gtk_widget_queue_draw (c_tool->graph);
gimp_image_map_tool_preview (GIMP_IMAGE_MAP_TOOL (c_tool));
}
static gboolean
curves_set_sensitive_callback (gpointer item_data,
GimpCurvesTool *c_tool)
{
GimpHistogramChannel channel = GPOINTER_TO_INT (item_data);
switch (channel)
{
case GIMP_HISTOGRAM_VALUE:
return TRUE;
case GIMP_HISTOGRAM_RED:
case GIMP_HISTOGRAM_GREEN:
case GIMP_HISTOGRAM_BLUE:
return c_tool->color;
case GIMP_HISTOGRAM_ALPHA:
return gimp_drawable_has_alpha (GIMP_IMAGE_MAP_TOOL (c_tool)->drawable);
}
return FALSE;
}
static void
curves_curve_type_callback (GtkWidget *widget,
GimpCurvesTool *c_tool)
{
GimpCurveType curve_type;
gimp_radio_button_update (widget, &curve_type);
if (c_tool->curves->curve_type[c_tool->channel] != curve_type)
{
c_tool->curves->curve_type[c_tool->channel] = curve_type;
if (curve_type == GIMP_CURVE_SMOOTH)
{
gint i;
gint32 index;
/* pick representative points from the curve
* and make them control points
*/
for (i = 0; i <= 8; i++)
{
index = CLAMP0255 (i * 32);
c_tool->curves->points[c_tool->channel][i * 2][0] = index;
c_tool->curves->points[c_tool->channel][i * 2][1] = c_tool->curves->curve[c_tool->channel][index];
}
}
curves_calculate_curve (c_tool->curves, c_tool->channel);
curves_update (c_tool, XRANGE_TOP);
gtk_widget_queue_draw (c_tool->graph);
gimp_image_map_tool_preview (GIMP_IMAGE_MAP_TOOL (c_tool));
}
}
static gboolean
curves_graph_events (GtkWidget *widget,
GdkEvent *event,
GimpCurvesTool *c_tool)
{
static GdkCursorType cursor_type = GDK_TOP_LEFT_ARROW;
GdkCursorType new_cursor = GDK_X_CURSOR;
GdkEventButton *bevent;
GdkEventMotion *mevent;
gint i;
gint tx, ty;
gint x, y;
gint closest_point;
gint distance;
gint x1, x2, y1, y2;
if (event->type == GDK_EXPOSE)
{
curves_graph_expose (widget, &((GdkEventExpose *) event)->area, c_tool);
return TRUE;
}
/* get the pointer position */
gdk_window_get_pointer (c_tool->graph->window, &tx, &ty, NULL);
x = CLAMP ((tx - RADIUS), 0, 255);
y = CLAMP ((ty - RADIUS), 0, 255);
distance = G_MAXINT;
for (i = 0, closest_point = 0; i < 17; i++)
{
if (c_tool->curves->points[c_tool->channel][i][0] != -1)
if (abs (x - c_tool->curves->points[c_tool->channel][i][0]) < distance)
{
distance = abs (x - c_tool->curves->points[c_tool->channel][i][0]);
closest_point = i;
}
}
if (distance > MIN_DISTANCE)
closest_point = (x + 8) / 16;
switch (event->type)
{
case GDK_BUTTON_PRESS:
bevent = (GdkEventButton *) event;
new_cursor = GDK_TCROSS;
switch (c_tool->curves->curve_type[c_tool->channel])
{
case GIMP_CURVE_SMOOTH:
/* determine the leftmost and rightmost points */
c_tool->leftmost = -1;
for (i = closest_point - 1; i >= 0; i--)
if (c_tool->curves->points[c_tool->channel][i][0] != -1)
{
c_tool->leftmost = c_tool->curves->points[c_tool->channel][i][0];
break;
}
c_tool->rightmost = 256;
for (i = closest_point + 1; i < 17; i++)
if (c_tool->curves->points[c_tool->channel][i][0] != -1)
{
c_tool->rightmost = c_tool->curves->points[c_tool->channel][i][0];
break;
}
c_tool->grab_point = closest_point;
c_tool->curves->points[c_tool->channel][c_tool->grab_point][0] = x;
c_tool->curves->points[c_tool->channel][c_tool->grab_point][1] = 255 - y;
break;
case GIMP_CURVE_FREE:
c_tool->curves->curve[c_tool->channel][x] = 255 - y;
c_tool->grab_point = x;
c_tool->last = y;
break;
}
gtk_grab_add (widget);
curves_calculate_curve (c_tool->curves, c_tool->channel);
curves_update (c_tool, XRANGE_TOP);
gtk_widget_queue_draw (c_tool->graph);
return TRUE;
case GDK_BUTTON_RELEASE:
new_cursor = GDK_FLEUR;
c_tool->grab_point = -1;
gimp_image_map_tool_preview (GIMP_IMAGE_MAP_TOOL (c_tool));
gtk_grab_remove (widget);
return TRUE;
case GDK_MOTION_NOTIFY:
mevent = (GdkEventMotion *) event;
switch (c_tool->curves->curve_type[c_tool->channel])
{
case GIMP_CURVE_SMOOTH:
/* If no point is grabbed... */
if (c_tool->grab_point == -1)
{
if (c_tool->curves->points[c_tool->channel][closest_point][0] != -1)
new_cursor = GDK_FLEUR;
else
new_cursor = GDK_TCROSS;
}
/* Else, drag the grabbed point */
else
{
new_cursor = GDK_TCROSS;
c_tool->curves->points[c_tool->channel][c_tool->grab_point][0] = -1;
if (x > c_tool->leftmost && x < c_tool->rightmost)
{
closest_point = (x + 8) / 16;
if (c_tool->curves->points[c_tool->channel][closest_point][0] == -1)
c_tool->grab_point = closest_point;
c_tool->curves->points[c_tool->channel][c_tool->grab_point][0] = x;
c_tool->curves->points[c_tool->channel][c_tool->grab_point][1] = 255 - y;
}
curves_calculate_curve (c_tool->curves, c_tool->channel);
}
break;
case GIMP_CURVE_FREE:
if (c_tool->grab_point != -1)
{
if (c_tool->grab_point > x)
{
x1 = x;
x2 = c_tool->grab_point;
y1 = y;
y2 = c_tool->last;
}
else
{
x1 = c_tool->grab_point;
x2 = x;
y1 = c_tool->last;
y2 = y;
}
if (x2 != x1)
for (i = x1; i <= x2; i++)
c_tool->curves->curve[c_tool->channel][i] = 255 - (y1 + ((y2 - y1) * (i - x1)) / (x2 - x1));
else
c_tool->curves->curve[c_tool->channel][x] = 255 - y;
c_tool->grab_point = x;
c_tool->last = y;
}
if (mevent->state & GDK_BUTTON1_MASK)
new_cursor = GDK_TCROSS;
else
new_cursor = GDK_PENCIL;
break;
}
if (new_cursor != cursor_type)
{
cursor_type = new_cursor;
gimp_cursor_set (c_tool->graph,
cursor_type,
GIMP_TOOL_CURSOR_NONE,
GIMP_CURSOR_MODIFIER_NONE);
}
curves_update (c_tool, XRANGE_TOP);
c_tool->cursor_x = tx - RADIUS;
c_tool->cursor_y = ty - RADIUS;
gtk_widget_queue_draw (c_tool->graph);
return TRUE;
case GDK_LEAVE_NOTIFY:
c_tool->cursor_x = -1;
c_tool->cursor_y = -1;
gtk_widget_queue_draw (c_tool->graph);
return TRUE;
default:
break;
}
return FALSE;
}
static void
curve_print_loc (GimpCurvesTool *c_tool)
{
gchar buf[32];
gint x, y;
gint w, h;
if (c_tool->cursor_x < 0 || c_tool->cursor_x > 255)
return;
if (c_tool->cursor_y < 0 || c_tool->cursor_y > 255)
return;
if (! c_tool->cursor_layout)
{
c_tool->cursor_layout = gtk_widget_create_pango_layout (c_tool->graph,
"x:888 y:888");
pango_layout_get_pixel_extents (c_tool->cursor_layout,
NULL, &c_tool->cursor_rect);
}
x = RADIUS * 2 + 2;
y = RADIUS * 2 + 2;
w = c_tool->cursor_rect.width + 4;
h = c_tool->cursor_rect.height + 4;
gdk_draw_rectangle (c_tool->graph->window,
c_tool->graph->style->bg_gc[GTK_STATE_ACTIVE],
TRUE,
x, y, w + 1, h + 1);
gdk_draw_rectangle (c_tool->graph->window,
c_tool->graph->style->black_gc,
FALSE,
x, y, w, h);
g_snprintf (buf, sizeof (buf), "x:%3d y:%3d",
c_tool->cursor_x, 255 - c_tool->cursor_y);
pango_layout_set_text (c_tool->cursor_layout, buf, 11);
gdk_draw_layout (c_tool->graph->window,
c_tool->graph->style->black_gc,
x + 2, y + 2,
c_tool->cursor_layout);
}
static void
curves_graph_expose (GtkWidget *widget,
GdkRectangle *area,
GimpCurvesTool *c_tool)
{
GimpHistogramChannel sel_channel;
gchar buf[32];
gint offset;
gint height;
gint i;
GdkPoint points[256];
if (c_tool->color)
{
sel_channel = c_tool->channel;
}
else
{
if (c_tool->channel == 2)
sel_channel = GIMP_HISTOGRAM_ALPHA;
else
sel_channel = GIMP_HISTOGRAM_VALUE;
}
/* Draw the grid lines */
for (i = 0; i < 5; i++)
{
gdk_draw_line (widget->window,
c_tool->graph->style->dark_gc[GTK_STATE_NORMAL],
RADIUS, i * (GRAPH_HEIGHT / 4) + RADIUS,
GRAPH_WIDTH + RADIUS, i * (GRAPH_HEIGHT / 4) + RADIUS);
gdk_draw_line (widget->window,
c_tool->graph->style->dark_gc[GTK_STATE_NORMAL],
i * (GRAPH_WIDTH / 4) + RADIUS, RADIUS,
i * (GRAPH_WIDTH / 4) + RADIUS, GRAPH_HEIGHT + RADIUS);
}
/* Draw the curve */
for (i = 0; i < 256; i++)
{
points[i].x = i + RADIUS;
points[i].y = 255 - c_tool->curves->curve[c_tool->channel][i] + RADIUS;
}
if (c_tool->curves->curve_type[c_tool->channel] == GIMP_CURVE_FREE)
{
gdk_draw_points (widget->window,
c_tool->graph->style->black_gc,
points, 256);
}
else
{
gdk_draw_lines (widget->window,
c_tool->graph->style->black_gc,
points, 256);
/* Draw the points */
for (i = 0; i < 17; i++)
{
if (c_tool->curves->points[c_tool->channel][i][0] != -1)
gdk_draw_arc (widget->window,
c_tool->graph->style->black_gc,
TRUE,
c_tool->curves->points[c_tool->channel][i][0],
255 - c_tool->curves->points[c_tool->channel][i][1],
RADIUS * 2, RADIUS * 2, 0, 23040);
}
}
if (c_tool->col_value[sel_channel] >= 0)
{
/* draw the color line */
gdk_draw_line (widget->window,
c_tool->graph->style->black_gc,
c_tool->col_value[sel_channel] + RADIUS,
RADIUS,
c_tool->col_value[sel_channel] + RADIUS,
GRAPH_HEIGHT + RADIUS);
/* and xpos indicator */
g_snprintf (buf, sizeof (buf), "x:%d",
c_tool->col_value[sel_channel]);
if (! c_tool->xpos_layout)
c_tool->xpos_layout = gtk_widget_create_pango_layout (c_tool->graph,
buf);
else
pango_layout_set_text (c_tool->xpos_layout, buf, -1);
pango_layout_get_pixel_size (c_tool->xpos_layout, &offset, &height);
if ((c_tool->col_value[sel_channel] + RADIUS) < 127)
offset = RADIUS + 4;
else
offset = - (offset + 2);
gdk_draw_layout (widget->window,
c_tool->graph->style->black_gc,
c_tool->col_value[sel_channel] + offset,
GRAPH_HEIGHT - height - 2,
c_tool->xpos_layout);
}
curve_print_loc (c_tool);
}
static void
curves_load_callback (GtkWidget *widget,
GimpCurvesTool *c_tool)
{
if (! c_tool->file_dialog)
file_dialog_create (c_tool);
else if (GTK_WIDGET_VISIBLE (c_tool->file_dialog))
return;
c_tool->is_save = FALSE;
gtk_window_set_title (GTK_WINDOW (c_tool->file_dialog), _("Load Curves"));
gtk_widget_show (c_tool->file_dialog);
}
static void
curves_save_callback (GtkWidget *widget,
GimpCurvesTool *c_tool)
{
if (! c_tool->file_dialog)
file_dialog_create (c_tool);
else if (GTK_WIDGET_VISIBLE (c_tool->file_dialog))
return;
c_tool->is_save = TRUE;
gtk_window_set_title (GTK_WINDOW (c_tool->file_dialog), _("Save Curves"));
gtk_widget_show (c_tool->file_dialog);
}
static void
file_dialog_create (GimpCurvesTool *c_tool)
{
GtkFileSelection *file_dlg;
gchar *temp;
c_tool->file_dialog = gtk_file_selection_new ("");
file_dlg = GTK_FILE_SELECTION (c_tool->file_dialog);
gtk_window_set_role (GTK_WINDOW (file_dlg), "gimp-load-save-curves");
gtk_window_set_position (GTK_WINDOW (file_dlg), GTK_WIN_POS_MOUSE);
gtk_container_set_border_width (GTK_CONTAINER (file_dlg), 2);
gtk_container_set_border_width (GTK_CONTAINER (file_dlg->button_area), 2);
g_object_add_weak_pointer (G_OBJECT (file_dlg),
(gpointer) &c_tool->file_dialog);
gtk_window_set_transient_for (GTK_WINDOW (file_dlg),
GTK_WINDOW (GIMP_IMAGE_MAP_TOOL (c_tool)->shell));
gtk_window_set_destroy_with_parent (GTK_WINDOW (file_dlg), TRUE);
g_signal_connect_swapped (file_dlg->ok_button, "clicked",
G_CALLBACK (file_dialog_ok_callback),
c_tool);
g_signal_connect_swapped (file_dlg->cancel_button, "clicked",
G_CALLBACK (gtk_widget_destroy),
file_dlg);
temp = g_build_filename (gimp_directory (), "curves", ".", NULL);
gtk_file_selection_set_filename (file_dlg, temp);
g_free (temp);
gimp_help_connect (c_tool->file_dialog, gimp_standard_help_func,
GIMP_TOOL (c_tool)->tool_info->help_id, NULL);
}
static void
file_dialog_ok_callback (GimpCurvesTool *c_tool)
{
FILE *file = NULL;
const gchar *filename;
filename =
gtk_file_selection_get_filename (GTK_FILE_SELECTION (c_tool->file_dialog));
file = fopen (filename, c_tool->is_save ? "wt" : "rt");
if (! file)
{
g_message (c_tool->is_save ?
_("Could not open '%s' for writing: %s") :
_("Could not open '%s' for reading: %s"),
filename, g_strerror (errno));
return;
}
if (c_tool->is_save)
{
curves_write_to_file (c_tool, file);
}
else if (! curves_read_from_file (c_tool, file))
{
g_message ("Error in reading file '%s'.", filename);
}
if (file)
fclose (file);
gtk_widget_destroy (c_tool->file_dialog);
}
static gboolean
curves_read_from_file (GimpCurvesTool *c_tool,
FILE *file)
{
gint i, j;
gint fields;
gchar buf[50];
gint index[5][17];
gint value[5][17];
if (! fgets (buf, 50, file))
return FALSE;
if (strcmp (buf, "# GIMP Curves File\n") != 0)
return FALSE;
for (i = 0; i < 5; i++)
{
for (j = 0; j < 17; j++)
{
fields = fscanf (file, "%d %d ", &index[i][j], &value[i][j]);
if (fields != 2)
{
g_print ("fields != 2");
return FALSE;
}
}
}
for (i = 0; i < 5; i++)
{
c_tool->curves->curve_type[i] = GIMP_CURVE_SMOOTH;
for (j = 0; j < 17; j++)
{
c_tool->curves->points[i][j][0] = index[i][j];
c_tool->curves->points[i][j][1] = value[i][j];
}
}
for (i = 0; i < 5; i++)
curves_calculate_curve (c_tool->curves, i);
curves_update (c_tool, ALL);
gtk_widget_queue_draw (c_tool->graph);
gimp_radio_group_set_active (GTK_RADIO_BUTTON (c_tool->curve_type),
GINT_TO_POINTER (GIMP_CURVE_SMOOTH));
gimp_image_map_tool_preview (GIMP_IMAGE_MAP_TOOL (c_tool));
return TRUE;
}
static void
curves_write_to_file (GimpCurvesTool *c_tool,
FILE *file)
{
gint i, j;
gint32 index;
for (i = 0; i < 5; i++)
if (c_tool->curves->curve_type[i] == GIMP_CURVE_FREE)
{
/* pick representative points from the curve
and make them control points */
for (j = 0; j <= 8; j++)
{
index = CLAMP0255 (j * 32);
c_tool->curves->points[i][j * 2][0] = index;
c_tool->curves->points[i][j * 2][1] = c_tool->curves->curve[i][index];
}
}
fprintf (file, "# GIMP Curves File\n");
for (i = 0; i < 5; i++)
{
for (j = 0; j < 17; j++)
fprintf (file, "%d %d ",
c_tool->curves->points[i][j][0],
c_tool->curves->points[i][j][1]);
fprintf (file, "\n");
}
}