gimpcurvestool.c 43.9 KB
Newer Older
Elliot Lee's avatar
Elliot Lee committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
/* 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
16
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
Elliot Lee's avatar
Elliot Lee committed
17
 */
Sven Neumann's avatar
Sven Neumann committed
18

19 20 21
#include "config.h"

#include <stdio.h>
22 23
#include <stdlib.h>
#include <string.h>
24
#include <errno.h>
25

26 27 28 29 30
#ifdef __GNUC__
#warning GTK_DISABLE_DEPRECATED
#endif
#undef GTK_DISABLE_DEPRECATED

Sven Neumann's avatar
Sven Neumann committed
31 32
#include <gtk/gtk.h>

33
#include "libgimpmath/gimpmath.h"
34
#include "libgimpbase/gimpbase.h"
35
#include "libgimpwidgets/gimpwidgets.h"
36

37
#include "tools-types.h"
Michael Natterer's avatar
Michael Natterer committed
38

39
#include "base/curves.h"
Michael Natterer's avatar
Michael Natterer committed
40 41 42 43 44
#include "base/gimphistogram.h"
#include "base/gimplut.h"

#include "core/gimpdrawable.h"
#include "core/gimpimage.h"
45
#include "core/gimpimagemap.h"
Michael Natterer's avatar
Michael Natterer committed
46 47

#include "widgets/gimpcursor.h"
48
#include "widgets/gimpenummenu.h"
Michael Natterer's avatar
Michael Natterer committed
49

50 51
#include "display/gimpdisplay.h"

Michael Natterer's avatar
Michael Natterer committed
52 53
#include "gimpcurvestool.h"
#include "tool_manager.h"
Sven Neumann's avatar
Sven Neumann committed
54

55 56
#include "libgimp/gimpintl.h"

57

58 59 60 61 62 63
#define GRAPH          (1 << 0)
#define XRANGE_TOP     (1 << 1)
#define XRANGE_BOTTOM  (1 << 2)
#define YRANGE         (1 << 3)
#define DRAW           (1 << 4)
#define ALL            0xFF
Elliot Lee's avatar
Elliot Lee committed
64

65
/*  NB: take care when changing these values: make sure the curve[] array in
66
 *  base/curves.h is large enough.
67
 */
68 69 70 71 72 73
#define GRAPH_WIDTH    256
#define GRAPH_HEIGHT   256
#define XRANGE_WIDTH   256
#define XRANGE_HEIGHT   16
#define YRANGE_WIDTH    16
#define YRANGE_HEIGHT  256
Elliot Lee's avatar
Elliot Lee committed
74 75 76
#define RADIUS           3
#define MIN_DISTANCE     8

77 78 79 80 81 82 83
#define GRAPH_MASK  (GDK_EXPOSURE_MASK            | \
		     GDK_POINTER_MOTION_MASK      | \
		     GDK_POINTER_MOTION_HINT_MASK | \
                     GDK_LEAVE_NOTIFY_MASK        | \
		     GDK_BUTTON_PRESS_MASK        | \
		     GDK_BUTTON_RELEASE_MASK      | \
		     GDK_BUTTON1_MOTION_MASK)
Elliot Lee's avatar
Elliot Lee committed
84 85


Michael Natterer's avatar
Michael Natterer committed
86 87 88
/*  local function prototypes  */

static void   gimp_curves_tool_class_init     (GimpCurvesToolClass *klass);
89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109
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_press   (GimpTool         *tool,
                                               GimpCoords       *coords,
                                               guint32           time,
					       GdkModifierType   state,
					       GimpDisplay      *gdisp);
static void   gimp_curves_tool_button_release (GimpTool         *tool,
                                               GimpCoords       *coords,
                                               guint32           time,
					       GdkModifierType   state,
					       GimpDisplay      *gdisp);
static void   gimp_curves_tool_motion         (GimpTool         *tool,
                                               GimpCoords       *coords,
                                               guint32           time,
					       GdkModifierType   state,
					       GimpDisplay      *gdisp);
110 111 112 113
static void   gimp_curves_tool_cursor_update  (GimpTool         *tool,
                                               GimpCoords       *coords,
                                               GdkModifierType   state,
                                               GimpDisplay      *gdisp);
114 115 116 117 118 119 120 121

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_color_update             (GimpTool         *tool,
					       GimpDisplay      *gdisp,
					       GimpDrawable     *drawable,
122
					       GimpCoords       *coords);
123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158
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,
                                               gpointer          data);
static void   curves_channel_reset_callback   (GtkWidget        *widget,
                                               gpointer          data);

static gboolean curves_set_sensitive_callback (gpointer          item_data,
                                               GimpCurvesTool   *c_tool);
static void   curves_smooth_callback          (GtkWidget        *widget,
                                               gpointer          data);
static void   curves_free_callback            (GtkWidget        *widget,
                                               gpointer          data);

static void   curves_load_callback            (GtkWidget        *widget,
                                               gpointer          data);
static void   curves_save_callback            (GtkWidget        *widget,
                                               gpointer          data);
static gint   curves_graph_events             (GtkWidget        *widget,
                                               GdkEvent         *event,
                                               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);

Michael Natterer's avatar
Michael Natterer committed
159

160 161
static GimpImageMapToolClass *parent_class = NULL;

Michael Natterer's avatar
Michael Natterer committed
162

163
/*  public functions  */
Michael Natterer's avatar
Michael Natterer committed
164 165

void
Nate Summers's avatar
Nate Summers committed
166
gimp_curves_tool_register (GimpToolRegisterCallback  callback,
167
                           gpointer                  data)
Michael Natterer's avatar
Michael Natterer committed
168
{
Nate Summers's avatar
Nate Summers committed
169
  (* callback) (GIMP_TYPE_CURVES_TOOL,
170 171
                NULL,
                FALSE,
172
                "gimp-curves-tool",
173 174
                _("Curves"),
                _("Adjust color curves"),
175
                N_("/Layer/Colors/Curves..."), NULL,
176
                NULL, "tools/curves.html",
Nate Summers's avatar
Nate Summers committed
177
                GIMP_STOCK_TOOL_CURVES,
178
                data);
Michael Natterer's avatar
Michael Natterer committed
179 180
}

181
GType
Michael Natterer's avatar
Michael Natterer committed
182 183
gimp_curves_tool_get_type (void)
{
184
  static GType tool_type = 0;
Michael Natterer's avatar
Michael Natterer committed
185 186 187

  if (! tool_type)
    {
188
      static const GTypeInfo tool_info =
Michael Natterer's avatar
Michael Natterer committed
189 190
      {
        sizeof (GimpCurvesToolClass),
191 192 193 194 195 196 197 198
	(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,
Michael Natterer's avatar
Michael Natterer committed
199 200
      };

201 202 203
      tool_type = g_type_register_static (GIMP_TYPE_IMAGE_MAP_TOOL,
					  "GimpCurvesTool", 
                                          &tool_info, 0);
Michael Natterer's avatar
Michael Natterer committed
204 205 206 207 208
    }

  return tool_type;
}

209 210 211

/*  private functions  */

Michael Natterer's avatar
Michael Natterer committed
212 213 214
static void
gimp_curves_tool_class_init (GimpCurvesToolClass *klass)
{
215 216 217
  GObjectClass          *object_class;
  GimpToolClass         *tool_class;
  GimpImageMapToolClass *image_map_tool_class;
Michael Natterer's avatar
Michael Natterer committed
218

219 220 221
  object_class         = G_OBJECT_CLASS (klass);
  tool_class           = GIMP_TOOL_CLASS (klass);
  image_map_tool_class = GIMP_IMAGE_MAP_TOOL_CLASS (klass);
Michael Natterer's avatar
Michael Natterer committed
222

223
  parent_class = g_type_class_peek_parent (klass);
Michael Natterer's avatar
Michael Natterer committed
224

225
  object_class->finalize = gimp_curves_tool_finalize;
226

227 228 229 230 231
  tool_class->initialize     = gimp_curves_tool_initialize;
  tool_class->button_press   = gimp_curves_tool_button_press;
  tool_class->button_release = gimp_curves_tool_button_release;
  tool_class->motion         = gimp_curves_tool_motion;
  tool_class->cursor_update  = gimp_curves_tool_cursor_update;
232 233 234 235

  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;
Michael Natterer's avatar
Michael Natterer committed
236 237 238
}

static void
239
gimp_curves_tool_init (GimpCurvesTool *c_tool)
Michael Natterer's avatar
Michael Natterer committed
240
{
241 242 243 244 245
  GimpImageMapTool *image_map_tool;
  gint              i;

  image_map_tool = GIMP_IMAGE_MAP_TOOL (c_tool);

246
  image_map_tool->shell_desc  = _("Adjust Color Curves");
247

248 249 250
  c_tool->curves  = g_new0 (Curves, 1);
  c_tool->lut     = gimp_lut_new ();
  c_tool->channel = GIMP_HISTOGRAM_VALUE;
251 252 253 254

  curves_init (c_tool->curves);

  for (i = 0;
255
       i < (sizeof (c_tool->col_value) / sizeof (c_tool->col_value[0]));
256
       i++)
257 258
    c_tool->col_value[i] = -1;

Michael Natterer's avatar
Michael Natterer committed
259 260 261
}

static void
262
gimp_curves_tool_finalize (GObject *object)
Michael Natterer's avatar
Michael Natterer committed
263
{
264
  GimpCurvesTool *c_tool;
Michael Natterer's avatar
Michael Natterer committed
265

266 267 268
  c_tool = GIMP_CURVES_TOOL (object);

  if (c_tool->curves)
Michael Natterer's avatar
Michael Natterer committed
269
    {
270 271
      g_free(c_tool->curves);
      c_tool->curves = NULL;
Michael Natterer's avatar
Michael Natterer committed
272 273
    }

274
  if (c_tool->lut)
Michael Natterer's avatar
Michael Natterer committed
275
    {
276 277
      gimp_lut_free (c_tool->lut);
      c_tool->lut = NULL;
Michael Natterer's avatar
Michael Natterer committed
278 279
    }

280
  if (c_tool->pixmap)
Michael Natterer's avatar
Michael Natterer committed
281
    {
282 283
      g_object_unref (c_tool->pixmap);
      c_tool->pixmap = NULL;
Michael Natterer's avatar
Michael Natterer committed
284
    }
285 286

  if (c_tool->cursor_layout)
Michael Natterer's avatar
Michael Natterer committed
287
    {
288 289
      g_object_unref (c_tool->cursor_layout);
      c_tool->cursor_layout = NULL;
Michael Natterer's avatar
Michael Natterer committed
290 291
    }

292 293 294 295 296
  if (c_tool->xpos_layout)
    {
      g_object_unref (c_tool->xpos_layout);
      c_tool->xpos_layout = NULL;
    }
Michael Natterer's avatar
Michael Natterer committed
297

298
  G_OBJECT_CLASS (parent_class)->finalize (object);
Michael Natterer's avatar
Michael Natterer committed
299 300 301
}

static void
302 303
gimp_curves_tool_initialize (GimpTool    *tool,
			     GimpDisplay *gdisp)
Michael Natterer's avatar
Michael Natterer committed
304
{
305 306 307 308
  GimpCurvesTool *c_tool;
  GimpDrawable   *drawable;

  c_tool = GIMP_CURVES_TOOL (tool);
309 310

  if (gimp_drawable_is_indexed (gimp_image_active_drawable (gdisp->gimage)))
Michael Natterer's avatar
Michael Natterer committed
311
    {
312 313 314
      g_message (_("Curves for indexed drawables cannot be adjusted."));
      return;
    }
Michael Natterer's avatar
Michael Natterer committed
315

316
  drawable = gimp_image_active_drawable (gdisp->gimage);
Michael Natterer's avatar
Michael Natterer committed
317

318
  curves_init (c_tool->curves);
Michael Natterer's avatar
Michael Natterer committed
319

320 321 322 323 324 325 326 327 328 329 330 331 332 333
  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);

  /* 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 */
334 335
  gimp_option_menu_set_history (GTK_OPTION_MENU (c_tool->channel_menu),
                                GINT_TO_POINTER (c_tool->channel));
Michael Natterer's avatar
Michael Natterer committed
336

337
  curves_update (c_tool, ALL);
Michael Natterer's avatar
Michael Natterer committed
338 339 340
}

static void
341 342 343 344 345
gimp_curves_tool_button_press (GimpTool        *tool,
                               GimpCoords      *coords,
                               guint32          time,
			       GdkModifierType  state,
			       GimpDisplay     *gdisp)
Michael Natterer's avatar
Michael Natterer committed
346
{
347 348 349 350
  GimpCurvesTool *c_tool;
  GimpDrawable   *drawable;

  c_tool = GIMP_CURVES_TOOL (tool);
Michael Natterer's avatar
Michael Natterer committed
351 352 353 354 355

  drawable = gimp_image_active_drawable (gdisp->gimage);

  tool->gdisp = gdisp;

356 357 358
  g_assert (drawable == tool->drawable);

#if 0
Michael Natterer's avatar
Michael Natterer committed
359 360
  if (drawable != tool->drawable)
    {
361
      gimp_tool_control_set_preserve (tool->control, TRUE);
362
      gimp_image_map_abort (GIMP_IMAGE_MAP_TOOL (tool)->image_map);
363
      gimp_tool_control_set_preserve (tool->control, FALSE);
Michael Natterer's avatar
Michael Natterer committed
364 365 366

      tool->drawable = drawable;

367 368 369
      c_tool->color  = gimp_drawable_is_rgb (drawable);

      GIMP_IMAGE_MAP_TOOL (tool)->drawable  = drawable;
370 371
      GIMP_IMAGE_MAP_TOOL (tool)->image_map = gimp_image_map_new (TRUE,
                                                                  drawable);
372 373

      gimp_option_menu_set_sensitive 
374
        (GTK_OPTION_MENU (c_tool->channel_menu),
375
         (GimpOptionMenuSensitivityCallback) curves_set_sensitive_callback,
376
         c_tool);
Michael Natterer's avatar
Michael Natterer committed
377
    }
378
#endif
Michael Natterer's avatar
Michael Natterer committed
379

380
  gimp_tool_control_activate (tool->control);
Michael Natterer's avatar
Michael Natterer committed
381

382
  curves_color_update (tool, gdisp, drawable, coords);
383
  curves_update (c_tool, GRAPH | DRAW);
Michael Natterer's avatar
Michael Natterer committed
384 385 386
}

static void
387 388 389 390 391
gimp_curves_tool_button_release (GimpTool        *tool,
                                 GimpCoords      *coords,
                                 guint32          time,
				 GdkModifierType  state,
				 GimpDisplay     *gdisp)
Michael Natterer's avatar
Michael Natterer committed
392
{
393 394
  GimpCurvesTool *c_tool;
  GimpDrawable   *drawable;
Michael Natterer's avatar
Michael Natterer committed
395

396 397 398
  c_tool = GIMP_CURVES_TOOL (tool);

  drawable = gimp_image_active_drawable (gdisp->gimage);
Michael Natterer's avatar
Michael Natterer committed
399

400
  curves_color_update (tool, gdisp, drawable, coords);
Michael Natterer's avatar
Michael Natterer committed
401

402
  if (state & GDK_SHIFT_MASK)
Michael Natterer's avatar
Michael Natterer committed
403
    {
404
      curves_add_point (c_tool, coords->x, coords->y, c_tool->channel);
405
      curves_calculate_curve (c_tool->curves, c_tool->channel);
Michael Natterer's avatar
Michael Natterer committed
406
    }
407
  else if (state & GDK_CONTROL_MASK)
Michael Natterer's avatar
Michael Natterer committed
408
    {
409
      gint i;
410

411 412 413 414 415
      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);
        }
Michael Natterer's avatar
Michael Natterer committed
416 417
    }

418 419 420
  gimp_tool_control_halt (tool->control);

  curves_update (c_tool, GRAPH | DRAW);
Michael Natterer's avatar
Michael Natterer committed
421 422 423
}

static void
424 425 426 427 428
gimp_curves_tool_motion (GimpTool        *tool,
                         GimpCoords      *coords,
                         guint32          time,
			 GdkModifierType  state,
			 GimpDisplay     *gdisp)
Michael Natterer's avatar
Michael Natterer committed
429
{
430 431
  GimpCurvesTool *c_tool;
  GimpDrawable   *drawable;
Michael Natterer's avatar
Michael Natterer committed
432

433
  c_tool = GIMP_CURVES_TOOL (tool);
Elliot Lee's avatar
Elliot Lee committed
434

435
  drawable = gimp_image_active_drawable (gdisp->gimage);
Elliot Lee's avatar
Elliot Lee committed
436

437
  curves_color_update (tool, gdisp, drawable, coords);
438
  curves_update (c_tool, GRAPH | DRAW);
Elliot Lee's avatar
Elliot Lee committed
439 440
}

441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467
static void
gimp_curves_tool_cursor_update (GimpTool        *tool,
                                GimpCoords      *coords,
                                GdkModifierType  state,
                                GimpDisplay     *gdisp)
{
  if (gimp_display_coords_in_active_drawable (gdisp, coords))
    {
      gimp_tool_control_set_tool_cursor (tool->control,
                                         GIMP_COLOR_PICKER_TOOL_CURSOR);
      gimp_tool_control_set_cursor_modifier (tool->control,
                                             (state & (GDK_SHIFT_MASK |
                                                       GDK_CONTROL_MASK) ?
                                              GIMP_CURSOR_MODIFIER_PLUS  :
                                              GIMP_CURSOR_MODIFIER_NONE));
    }
  else
    {
      gimp_tool_control_set_tool_cursor (tool->control,
                                         GIMP_TOOL_CURSOR_NONE);
      gimp_tool_control_set_cursor_modifier (tool->control,
                                             GIMP_CURSOR_MODIFIER_NONE);
    }

  GIMP_TOOL_CLASS (parent_class)->cursor_update (tool, coords, state, gdisp);
}

BST 1999 Andy Thomas's avatar
BST 1999 Andy Thomas committed
468
static void
469 470 471
curves_color_update (GimpTool       *tool,
                     GimpDisplay    *gdisp,
                     GimpDrawable   *drawable,
472
                     GimpCoords     *coords)
BST 1999 Andy Thomas's avatar
BST 1999 Andy Thomas committed
473
{
474 475
  GimpCurvesTool *c_tool;
  guchar         *color;
476 477
  gint            x;
  gint            y;
478 479

  if (! (tool && gimp_tool_control_is_active (tool->control)))
BST 1999 Andy Thomas's avatar
BST 1999 Andy Thomas committed
480 481
    return;

482
  c_tool = GIMP_CURVES_TOOL (tool);
BST 1999 Andy Thomas's avatar
BST 1999 Andy Thomas committed
483

484
  gimp_drawable_offsets (drawable, &x, &y);
BST 1999 Andy Thomas's avatar
BST 1999 Andy Thomas committed
485

486 487
  x = RINT (coords->x) - x;
  y = RINT (coords->y) - y;
BST 1999 Andy Thomas's avatar
BST 1999 Andy Thomas committed
488

489 490 491 492 493
  color = gimp_image_map_get_color_at (GIMP_IMAGE_MAP_TOOL (tool)->image_map,
                                       x, y);
  if (color)
    {
      gint maxval;
BST 1999 Andy Thomas's avatar
BST 1999 Andy Thomas committed
494

495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512
      c_tool->col_value[GIMP_HISTOGRAM_RED]   = color[RED_PIX];
      c_tool->col_value[GIMP_HISTOGRAM_GREEN] = color[GREEN_PIX];
      c_tool->col_value[GIMP_HISTOGRAM_BLUE]  = color[BLUE_PIX];
      
      if (gimp_drawable_has_alpha (drawable))
        c_tool->col_value[GIMP_HISTOGRAM_ALPHA] = color[3];
      
      if (gimp_drawable_is_indexed (drawable))
        c_tool->col_value[GIMP_HISTOGRAM_ALPHA] = color[4];
      
      maxval = MAX (color[RED_PIX], color[GREEN_PIX]);
      c_tool->col_value[GIMP_HISTOGRAM_VALUE] = MAX (maxval, color[BLUE_PIX]);
      
      g_free (color);
    }
  else
    {
      gint i;
BST 1999 Andy Thomas's avatar
BST 1999 Andy Thomas committed
513

514 515 516 517 518
      for (i = 0; i <= GIMP_HISTOGRAM_ALPHA; i++)
        c_tool->col_value[i] = -1;
      
      return;
    }
519
}
BST 1999 Andy Thomas's avatar
BST 1999 Andy Thomas committed
520

521
static void
522 523 524 525
curves_add_point (GimpCurvesTool *c_tool,
		  gint            x,
		  gint            y,
		  gint            cchan)
526 527
{
  /* Add point onto the curve */
528 529 530 531
  gint closest_point = 0;
  gint distance;
  gint curvex;
  gint i;
532

533
  switch (c_tool->curves->curve_type[cchan])
534
    {
535 536
    case CURVES_SMOOTH:
      curvex   = c_tool->col_value[cchan];
537
      distance = G_MAXINT;
538

539 540
      for (i = 0; i < 17; i++)
	{
541 542
	  if (c_tool->curves->points[cchan][i][0] != -1)
	    if (abs (curvex - c_tool->curves->points[cchan][i][0]) < distance)
543
	      {
544
		distance = abs (curvex - c_tool->curves->points[cchan][i][0]);
545 546 547 548 549 550 551
		closest_point = i;
	      }
	}
      
      if (distance > MIN_DISTANCE)
	closest_point = (curvex + 8) / 16;
      
552 553
      c_tool->curves->points[cchan][closest_point][0] = curvex;
      c_tool->curves->points[cchan][closest_point][1] = c_tool->curves->curve[cchan][curvex];
554 555
      break;
      
556 557
    case CURVES_FREE:
      c_tool->curves->curve[cchan][x] = 255 - y;
558
      break;
559
    }
Elliot Lee's avatar
Elliot Lee committed
560 561
}

562 563
static void
gimp_curves_tool_map (GimpImageMapTool *image_map_tool)
Elliot Lee's avatar
Elliot Lee committed
564
{
565
  GimpCurvesTool *c_tool;
566

567
  c_tool = GIMP_CURVES_TOOL (image_map_tool);
568

569 570 571 572
  gimp_lut_setup (c_tool->lut, 
		  (GimpLutFunc) curves_lut_func,
                  c_tool->curves,
		  gimp_drawable_bytes (image_map_tool->drawable));
573

574 575 576
  gimp_image_map_apply (image_map_tool->image_map,
                        (GimpImageMapApplyFunc) gimp_lut_process_2,
                        c_tool->lut);
Elliot Lee's avatar
Elliot Lee committed
577 578
}

579

580 581 582
/*******************/
/*  Curves dialog  */
/*******************/
Elliot Lee's avatar
Elliot Lee committed
583

584 585
static void
gimp_curves_tool_dialog (GimpImageMapTool *image_map_tool)
Elliot Lee's avatar
Elliot Lee committed
586
{
587 588
  GimpCurvesTool *c_tool;
  GtkWidget      *hbox;
589
  GtkWidget      *vbox;
590 591 592 593
  GtkWidget      *hbbox;
  GtkWidget      *frame;
  GtkWidget      *table;
  GtkWidget      *button;
594

595
  c_tool = GIMP_CURVES_TOOL (image_map_tool);
BST 1999 Andy Thomas's avatar
BST 1999 Andy Thomas committed
596

597 598 599 600 601 602 603 604 605
  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);

606 607 608
  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);
609
  gtk_box_pack_start (GTK_BOX (vbox), table, FALSE, FALSE, 0);
610
  gtk_widget_show (table);
Elliot Lee's avatar
Elliot Lee committed
611

612 613
  /*  The option menu for selecting channels  */
  hbox = gtk_hbox_new (FALSE, 4);
614

615 616 617 618 619 620
  c_tool->channel_menu =
    gimp_enum_option_menu_new (GIMP_TYPE_HISTOGRAM_CHANNEL,
                               G_CALLBACK (curves_channel_callback),
                               c_tool);
  gtk_box_pack_start (GTK_BOX (hbox), c_tool->channel_menu, FALSE, FALSE, 0);
  gtk_widget_show (c_tool->channel_menu);
621

622
  button = gtk_button_new_with_mnemonic (_("R_eset Channel"));
623 624
  gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
  gtk_widget_show (button);
625

626 627 628
  g_signal_connect (G_OBJECT (button), "clicked",
                    G_CALLBACK (curves_channel_reset_callback),
                    c_tool);
629

630 631 632
  gimp_table_attach_aligned (GTK_TABLE (table), 0, 0,
                             _("Modify Curves for Channel:"), 1.0, 0.5,
                             hbox, 1, FALSE);
Elliot Lee's avatar
Elliot Lee committed
633

634 635 636
  /*  The option menu for selecting the drawing method  */
  c_tool->curve_type_menu =
    gimp_option_menu_new (FALSE,
Elliot Lee's avatar
Elliot Lee committed
637

638
                          _("Smooth"), curves_smooth_callback,
639
                          c_tool, GINT_TO_POINTER (CURVES_SMOOTH), NULL, TRUE,
Elliot Lee's avatar
Elliot Lee committed
640

641
                          _("Free"), curves_free_callback,
642
                          c_tool, GINT_TO_POINTER (CURVES_FREE), NULL, FALSE,
Elliot Lee's avatar
Elliot Lee committed
643

644
                          NULL);
Elliot Lee's avatar
Elliot Lee committed
645

646 647 648
  gimp_table_attach_aligned (GTK_TABLE (table), 0, 1,
                             _("Curve Type:"), 1.0, 0.5,
                             c_tool->curve_type_menu, 1, TRUE);
Elliot Lee's avatar
Elliot Lee committed
649 650

  /*  The table for the yrange and the graph  */
651 652 653 654
  hbox = gtk_hbox_new (FALSE, 0);
  gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
  gtk_widget_show (hbox);

Elliot Lee's avatar
Elliot Lee committed
655
  table = gtk_table_new (2, 2, FALSE);
656 657
  gtk_table_set_col_spacings (GTK_TABLE (table), 2);
  gtk_table_set_row_spacings (GTK_TABLE (table), 2);
658
  gtk_box_pack_start (GTK_BOX (hbox), table, TRUE, FALSE, 0);
Elliot Lee's avatar
Elliot Lee committed
659 660 661

  /*  The range drawing area  */
  frame = gtk_frame_new (NULL);
662
  gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
Elliot Lee's avatar
Elliot Lee committed
663 664 665 666
  gtk_table_attach (GTK_TABLE (table), frame, 0, 1, 0, 1,
		    GTK_EXPAND, GTK_EXPAND, 0, 0);
  gtk_widget_show (frame);

667 668 669 670 671
  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);

Elliot Lee's avatar
Elliot Lee committed
672 673
  /*  The curves graph  */
  frame = gtk_frame_new (NULL);
674
  gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
Elliot Lee's avatar
Elliot Lee committed
675
  gtk_table_attach (GTK_TABLE (table), frame, 1, 2, 0, 1,
676 677
		    GTK_SHRINK | GTK_FILL,
		    GTK_SHRINK | GTK_FILL, 0, 0);
678
  gtk_widget_show (frame);
Elliot Lee's avatar
Elliot Lee committed
679

680 681 682
  c_tool->graph = gtk_drawing_area_new ();
  gtk_widget_set_size_request (c_tool->graph,
                               GRAPH_WIDTH  + RADIUS * 2,
683
                               GRAPH_HEIGHT + RADIUS * 2);
684 685 686
  gtk_widget_set_events (c_tool->graph, GRAPH_MASK);
  gtk_container_add (GTK_CONTAINER (frame), c_tool->graph);
  gtk_widget_show (c_tool->graph);
687

688
  g_signal_connect (G_OBJECT (c_tool->graph), "event",
689
		    G_CALLBACK (curves_graph_events),
690
		    c_tool);
Elliot Lee's avatar
Elliot Lee committed
691 692 693

  /*  The range drawing area  */
  frame = gtk_frame_new (NULL);
694
  gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
Elliot Lee's avatar
Elliot Lee committed
695 696 697 698
  gtk_table_attach (GTK_TABLE (table), frame, 1, 2, 1, 2,
		    GTK_EXPAND, GTK_EXPAND, 0, 0);
  gtk_widget_show (frame);

699 700 701 702
  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);
703

704
  gtk_widget_show (table);
Elliot Lee's avatar
Elliot Lee committed
705

706
  /*  Horizontal button box for load / save  */
707 708 709 710
  frame = gtk_frame_new (_("All Channels"));
  gtk_box_pack_end (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
  gtk_widget_show (frame);

711
  hbbox = gtk_hbutton_box_new ();
712
  gtk_container_set_border_width (GTK_CONTAINER (hbbox), 2);
713
  gtk_box_set_spacing (GTK_BOX (hbbox), 4);
714
  gtk_button_box_set_layout (GTK_BUTTON_BOX (hbbox), GTK_BUTTONBOX_SPREAD);
715
  gtk_container_add (GTK_CONTAINER (frame), hbbox);
716
  gtk_widget_show (hbbox);
717

718
  button = gtk_button_new_from_stock (GTK_STOCK_OPEN);
719 720
  GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
  gtk_box_pack_start (GTK_BOX (hbbox), button, FALSE, FALSE, 0);
721
  gimp_help_set_help_data (button, _("Read curves settings from file"), NULL);
722 723
  gtk_widget_show (button);

724 725
  g_signal_connect (G_OBJECT (button), "clicked",
		    G_CALLBACK (curves_load_callback),
726
		    c_tool);
727

728
  button = gtk_button_new_from_stock (GTK_STOCK_SAVE);
729 730
  GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
  gtk_box_pack_start (GTK_BOX (hbbox), button, FALSE, FALSE, 0);
731
  gimp_help_set_help_data (button, _("Save curves settings to file"), NULL);
732 733
  gtk_widget_show (button);

734 735
  g_signal_connect (G_OBJECT (button), "clicked",
		    G_CALLBACK (curves_save_callback),
736
		    c_tool);
Elliot Lee's avatar
Elliot Lee committed
737 738
}

739
static void
740
gimp_curves_tool_reset (GimpImageMapTool *image_map_tool)
741
{
742 743
  GimpCurvesTool       *c_tool;
  GimpHistogramChannel  channel;
744 745 746 747 748

  c_tool = GIMP_CURVES_TOOL (image_map_tool);

  c_tool->grab_point = -1;

749 750 751 752
  for (channel =  GIMP_HISTOGRAM_VALUE;
       channel <= GIMP_HISTOGRAM_ALPHA;
       channel++)
    curves_channel_reset (c_tool->curves, channel);
753 754

  curves_update (c_tool, GRAPH | XRANGE_TOP | DRAW);
755 756
}

757
static void
758 759 760
curve_print_loc (GimpCurvesTool *c_tool,
		 gint            xpos,
		 gint            ypos)
761
{
762
  gchar buf[32];
763

764
  if (! c_tool->cursor_layout)
765
    {
766 767 768 769
      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);
770 771
    }
  
772
  if (xpos >= 0 && xpos <= 255 && ypos >=0 && ypos <= 255)
773
    {
774 775
      gdk_draw_rectangle (c_tool->graph->window, 
			  c_tool->graph->style->bg_gc[GTK_STATE_ACTIVE],
776
			  TRUE, 
777 778 779 780
                          RADIUS * 2 + 2, 
                          RADIUS * 2 + 2, 
			  c_tool->cursor_rect.width  + 4,
			  c_tool->cursor_rect.height + 5);
781

782 783
      gdk_draw_rectangle (c_tool->graph->window, 
			  c_tool->graph->style->black_gc,
784
			  FALSE, 
785 786 787 788
                          RADIUS * 2 + 2, 
                          RADIUS * 2 + 2, 
			  c_tool->cursor_rect.width  + 3, 
			  c_tool->cursor_rect.height + 4);
789 790
      
      g_snprintf (buf, sizeof (buf), "x:%3d y:%3d",xpos, ypos);
791 792 793 794 795 796
      pango_layout_set_text (c_tool->cursor_layout, buf, 11);
      gdk_draw_layout (c_tool->graph->window, 
                       c_tool->graph->style->black_gc,
		       RADIUS * 2 + 4 + c_tool->cursor_rect.x,
		       RADIUS * 2 + 5 + c_tool->cursor_rect.y,
		       c_tool->cursor_layout);
797 798 799
    }
}

800
/* TODO: preview alpha channel stuff correctly.  -- austin, 20/May/99 */
Elliot Lee's avatar
Elliot Lee committed
801
static void
802 803
curves_update (GimpCurvesTool *c_tool,
	       gint            update)
Elliot Lee's avatar
Elliot Lee committed
804
{
805 806 807 808 809
  GimpHistogramChannel sel_channel;
  gint                 i, j;
  gchar                buf[32];
  gint                 offset;
  gint                 height;
810
  
811
  if (c_tool->color)
812
    {
813
      sel_channel = c_tool->channel;
814
    }
815 816
  else 
    {
817
      if (c_tool->channel == 2)
818 819 820 821
        sel_channel = GIMP_HISTOGRAM_ALPHA;
      else
        sel_channel = GIMP_HISTOGRAM_VALUE;
    }
822

Elliot Lee's avatar
Elliot Lee committed
823 824
  if (update & XRANGE_TOP)
    {
825
      guchar buf[XRANGE_WIDTH * 3];
826

827
      switch (sel_channel)
828
	{
Michael Natterer's avatar
Michael Natterer committed
829 830
	case GIMP_HISTOGRAM_VALUE:
	case GIMP_HISTOGRAM_ALPHA:
831
	  for (i = 0; i < XRANGE_HEIGHT / 2; i++)
832
	    {
833
	      for (j = 0; j < XRANGE_WIDTH ; j++)
834
		{
835 836 837
		  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];
838
		}
839
	      gtk_preview_draw_row (GTK_PREVIEW (c_tool->xrange),
840
				    buf, 0, i, XRANGE_WIDTH);
841
	    }
842 843
	  break;

Michael Natterer's avatar
Michael Natterer committed
844 845 846
	case GIMP_HISTOGRAM_RED:
	case GIMP_HISTOGRAM_GREEN:
	case GIMP_HISTOGRAM_BLUE:
847
	  {
848
	    for (i = 0; i < XRANGE_HEIGHT / 2; i++)
849
	      {
850 851
		for (j = 0; j < XRANGE_WIDTH; j++)
		  {
852 853 854
		    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];
855
		  }
856
		gtk_preview_draw_row (GTK_PREVIEW (c_tool->xrange),
857
				      buf, 0, i, XRANGE_WIDTH);
858
	      }
859
	    break;
860 861
	  }

862
	default:
863
	  g_warning ("unknown channel type %d, can't happen!?!?",
864
		     c_tool->channel);
865
	  break;
866
	}
Elliot Lee's avatar
Elliot Lee committed
867 868

      if (update & DRAW)
869 870 871
        gtk_widget_queue_draw_area (c_tool->xrange,
                                    0, 0,
                                    XRANGE_WIDTH, XRANGE_HEIGHT / 2);
Elliot Lee's avatar
Elliot Lee committed
872
    }
873

Elliot Lee's avatar
Elliot Lee committed
874 875
  if (update & XRANGE_BOTTOM)
    {
876
      guchar buf[XRANGE_WIDTH * 3];
Elliot Lee's avatar
Elliot Lee committed
877 878

      for (i = 0; i < XRANGE_WIDTH; i++)
879 880 881 882 883
        {
          buf[i * 3 + 0] = i;
          buf[i * 3 + 1] = i;
          buf[i * 3 + 2] = i;
        }
Elliot Lee's avatar
Elliot Lee committed
884 885

      for (i = XRANGE_HEIGHT / 2; i < XRANGE_HEIGHT; i++)
886 887
	gtk_preview_draw_row (GTK_PREVIEW (c_tool->xrange),
                              buf, 0, i, XRANGE_WIDTH);
Elliot Lee's avatar
Elliot Lee committed
888 889

      if (update & DRAW)
890 891 892
        gtk_widget_queue_draw_area (c_tool->xrange,
                                    0, XRANGE_HEIGHT / 2,
                                    XRANGE_WIDTH, XRANGE_HEIGHT / 2);
Elliot Lee's avatar
Elliot Lee committed
893
    }
894

Elliot Lee's avatar
Elliot Lee committed
895 896
  if (update & YRANGE)
    {
897 898
      guchar buf[YRANGE_WIDTH * 3];
      guchar pix[3];
Elliot Lee's avatar
Elliot Lee committed
899 900 901

      for (i = 0; i < YRANGE_HEIGHT; i++)
	{
902
	  switch (sel_channel)
903
	    {
Michael Natterer's avatar
Michael Natterer committed
904 905
	    case GIMP_HISTOGRAM_VALUE:
	    case GIMP_HISTOGRAM_ALPHA:
906 907 908
	      pix[0] = pix[1] = pix[2] = (255 - i);
	      break;

Michael Natterer's avatar
Michael Natterer committed
909 910 911
	    case GIMP_HISTOGRAM_RED:
	    case GIMP_HISTOGRAM_GREEN:
	    case GIMP_HISTOGRAM_BLUE:
912
	      pix[0] = pix[1] = pix[2] = 0;
913
	      pix[sel_channel - 1] = (255 - i);