gimpcurvestool.c 43 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 22
#include "config.h"

#include <stdio.h>

Sven Neumann's avatar
Sven Neumann committed
23 24 25 26
#include <gtk/gtk.h>

#include "apptypes.h"

Elliot Lee's avatar
Elliot Lee committed
27 28 29 30
#include "appenv.h"
#include "cursorutil.h"
#include "drawable.h"
#include "gdisplay.h"
31
#include "gimphistogram.h"
32
#include "gimpui.h"
Elliot Lee's avatar
Elliot Lee committed
33
#include "curves.h"
34
#include "gimplut.h"
Elliot Lee's avatar
Elliot Lee committed
35

36
#include "libgimp/gimpenv.h"
37
#include "libgimp/gimpmath.h"
38

39 40
#include "libgimp/gimpintl.h"

41 42 43 44 45 46
#define GRAPH          0x1
#define XRANGE_TOP     0x2
#define XRANGE_BOTTOM  0x4
#define YRANGE         0x8
#define DRAW          0x10
#define ALL           0xFF
Elliot Lee's avatar
Elliot Lee committed
47

48 49 50
/*  NB: take care when changing these values: make sure the curve[] array in
 *  curves.h is large enough.
 */
51 52 53 54 55 56
#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
57 58 59 60 61 62 63 64 65 66 67 68 69 70
#define RADIUS           3
#define MIN_DISTANCE     8

#define RANGE_MASK  GDK_EXPOSURE_MASK | \
                    GDK_ENTER_NOTIFY_MASK

#define GRAPH_MASK  GDK_EXPOSURE_MASK | \
		    GDK_POINTER_MOTION_MASK | \
		    GDK_POINTER_MOTION_HINT_MASK | \
                    GDK_ENTER_NOTIFY_MASK | \
		    GDK_BUTTON_PRESS_MASK | \
		    GDK_BUTTON_RELEASE_MASK | \
		    GDK_BUTTON1_MOTION_MASK

71
/*  the curves structures  */
Elliot Lee's avatar
Elliot Lee committed
72

73
typedef struct _Curves Curves;
74

Elliot Lee's avatar
Elliot Lee committed
75 76
struct _Curves
{
77
  gint x, y;    /*  coords for last mouse click  */
Elliot Lee's avatar
Elliot Lee committed
78 79
};

80
typedef gdouble CRMatrix[4][4];
81

82
/*  the curves tool options  */
83
static ToolOptions  * curves_options = NULL;
84 85

/*  the curves dialog  */
86
static CurvesDialog * curves_dialog = NULL;
87

88 89 90 91
/*  the curves file dialog  */
static GtkWidget *file_dlg = NULL;
static gboolean   load_save;

92 93
static GtkWidget *channel_items[5];

94 95 96 97 98 99 100 101
static CRMatrix CR_basis =
{
  { -0.5,  1.5, -1.5,  0.5 },
  {  1.0, -2.5,  2.0, -0.5 },
  { -0.5,  0.0,  0.5,  0.0 },
  {  0.0,  1.0,  0.0,  0.0 },
};

Elliot Lee's avatar
Elliot Lee committed
102

103
/*  curves action functions  */
Michael Natterer's avatar
Michael Natterer committed
104

Elliot Lee's avatar
Elliot Lee committed
105 106 107
static void   curves_button_press   (Tool *, GdkEventButton *, gpointer);
static void   curves_button_release (Tool *, GdkEventButton *, gpointer);
static void   curves_motion         (Tool *, GdkEventMotion *, gpointer);
Michael Natterer's avatar
Michael Natterer committed
108 109
static void   curves_control        (Tool *, ToolAction,       gpointer);

110
static CurvesDialog * curves_dialog_new (void);
111

112 113 114 115 116 117
static void   curves_update           (CurvesDialog *, int);
static void   curves_plot_curve       (CurvesDialog *, int, int, int, int);
static void   curves_preview          (CurvesDialog *);

static void   curves_channel_callback (GtkWidget *, gpointer);

118 119
static void   curves_smooth_callback      (GtkWidget *, gpointer);
static void   curves_free_callback        (GtkWidget *, gpointer);
120

121
static void   curves_channel_reset        (int);
122 123 124 125 126 127 128 129 130 131
static void   curves_reset_callback       (GtkWidget *, gpointer);
static void   curves_ok_callback          (GtkWidget *, gpointer);
static void   curves_cancel_callback      (GtkWidget *, gpointer);
static void   curves_load_callback        (GtkWidget *, gpointer);
static void   curves_save_callback        (GtkWidget *, gpointer);
static void   curves_preview_update       (GtkWidget *, gpointer);
static gint   curves_xrange_events        (GtkWidget *, GdkEvent *, CurvesDialog *);
static gint   curves_yrange_events        (GtkWidget *, GdkEvent *, CurvesDialog *);
static gint   curves_graph_events         (GtkWidget *, GdkEvent *, CurvesDialog *);
static void   curves_CR_compose           (CRMatrix, CRMatrix, CRMatrix);
132

133 134 135
static void   file_dialog_create          (GtkWidget *);
static void   file_dialog_ok_callback     (GtkWidget *, gpointer);
static void   file_dialog_cancel_callback (GtkWidget *, gpointer);
136

137 138
static gboolean  curves_read_from_file    (FILE *f);
static void      curves_write_to_file     (FILE *f);
139 140


Elliot Lee's avatar
Elliot Lee committed
141 142
/*  curves machinery  */

143
gfloat
Manish Singh's avatar
Manish Singh committed
144
curves_lut_func (CurvesDialog *cd,
145 146 147
		 gint          nchannels,
		 gint          channel,
		 gfloat        value)
Elliot Lee's avatar
Elliot Lee committed
148
{
149 150 151 152
  gfloat f;
  gint index;
  gdouble inten;
  gint j;
Elliot Lee's avatar
Elliot Lee committed
153

154 155 156 157
  if (nchannels == 1)
    j = 0;
  else
    j = channel + 1;
158

159
  inten = value;
160

Manish Singh's avatar
Manish Singh committed
161
  /* For color images this runs through the loop with j = channel +1
162 163 164 165 166 167 168 169 170 171 172 173 174 175 176
     the first time and j = 0 the second time */
  /* For bw images this runs through the loop with j = 0 the first and
     only time  */
  for (; j >= 0; j -= (channel + 1))
  {
    /* don't apply the overall curve to the alpha channel */
    if (j == 0 && (nchannels == 2 || nchannels == 4)
	&& channel == nchannels -1)
      return inten;

    if (inten < 0.0)
      inten = cd->curve[j][0]/255.0;
    else if (inten >= 1.0)
      inten = cd->curve[j][255]/255.0;
    else /* interpolate the curve */
Elliot Lee's avatar
Elliot Lee committed
177
    {
178 179 180 181
      index = floor(inten * 255.0);
      f = inten*255.0 - index;
      inten = ((1.0 - f) * cd->curve[j][index    ] + 
	       (      f) * cd->curve[j][index + 1] ) / 255.0;
Elliot Lee's avatar
Elliot Lee committed
182
    }
183 184
  }
  return inten;
Elliot Lee's avatar
Elliot Lee committed
185 186
}

BST 1999 Andy Thomas's avatar
BST 1999 Andy Thomas committed
187
static void
Michael Natterer's avatar
Michael Natterer committed
188 189 190 191 192
curves_colour_update (Tool           *tool,
		      GDisplay       *gdisp,
		      GimpDrawable   *drawable,
		      gint            x,
		      gint            y)
BST 1999 Andy Thomas's avatar
BST 1999 Andy Thomas committed
193
{
194 195 196 197 198 199 200
  guchar *color;
  gint    offx;
  gint    offy;
  gint    maxval;
  gboolean       has_alpha;
  gboolean       is_indexed;
  GimpImageType  sample_type;
BST 1999 Andy Thomas's avatar
BST 1999 Andy Thomas committed
201 202 203 204 205 206 207 208 209 210 211 212

  if(!tool || tool->state != ACTIVE)
    return;

  drawable_offsets (drawable, &offx, &offy);

  x -= offx;
  y -= offy;

  if (!(color = image_map_get_color_at(curves_dialog->image_map, x, y)))
    return;

213
  sample_type = gimp_drawable_type (drawable);
214 215
  is_indexed  = gimp_drawable_is_indexed (drawable);
  has_alpha   = GIMP_IMAGE_TYPE_HAS_ALPHA (sample_type);
BST 1999 Andy Thomas's avatar
BST 1999 Andy Thomas committed
216

Michael Natterer's avatar
Michael Natterer committed
217 218 219
  curves_dialog->col_value[GIMP_HISTOGRAM_RED] = color[RED_PIX];
  curves_dialog->col_value[GIMP_HISTOGRAM_GREEN] = color[GREEN_PIX];
  curves_dialog->col_value[GIMP_HISTOGRAM_BLUE] = color[BLUE_PIX];
BST 1999 Andy Thomas's avatar
BST 1999 Andy Thomas committed
220 221 222

  if (has_alpha)
    {
Michael Natterer's avatar
Michael Natterer committed
223
      curves_dialog->col_value [GIMP_HISTOGRAM_ALPHA] = color[3];
BST 1999 Andy Thomas's avatar
BST 1999 Andy Thomas committed
224 225 226
    }

  if (is_indexed)
Michael Natterer's avatar
Michael Natterer committed
227
    curves_dialog->col_value [GIMP_HISTOGRAM_ALPHA] = color[4];
BST 1999 Andy Thomas's avatar
BST 1999 Andy Thomas committed
228

229
  maxval = MAX (color[RED_PIX], color[GREEN_PIX]);
Michael Natterer's avatar
Michael Natterer committed
230
  curves_dialog->col_value[GIMP_HISTOGRAM_VALUE] = MAX (maxval, color[BLUE_PIX]);
BST 1999 Andy Thomas's avatar
BST 1999 Andy Thomas committed
231

232
  g_free (color);
233
}
BST 1999 Andy Thomas's avatar
BST 1999 Andy Thomas committed
234

235
static void
236 237 238 239
curves_add_point (GimpDrawable *drawable,
		  gint          x,
		  gint          y,
		  gint          cchan)
240 241
{
  /* Add point onto the curve */
242 243 244 245
  gint closest_point = 0;
  gint distance;
  gint curvex;
  gint i;
246

247
  switch (curves_dialog->curve_type[cchan])
248
    {
249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271
    case SMOOTH:
      curvex = curves_dialog->col_value[cchan];
      distance = G_MAXINT;
      for (i = 0; i < 17; i++)
	{
	  if (curves_dialog->points[cchan][i][0] != -1)
	    if (abs (curvex - curves_dialog->points[cchan][i][0]) < distance)
	      {
		distance = abs (curvex - curves_dialog->points[cchan][i][0]);
		closest_point = i;
	      }
	}
      
      if (distance > MIN_DISTANCE)
	closest_point = (curvex + 8) / 16;
      
      curves_dialog->points[cchan][closest_point][0] = curvex;
      curves_dialog->points[cchan][closest_point][1] = curves_dialog->curve[cchan][curvex];
      break;
      
    case GFREE:
      curves_dialog->curve[cchan][x] = 255 - y;
      break;
272
    }
Elliot Lee's avatar
Elliot Lee committed
273 274
}

275 276 277 278 279 280 281 282
/*  curves action functions  */

static void
curves_button_press (Tool           *tool,
		     GdkEventButton *bevent,
		     gpointer        gdisp_ptr)
{
  gint x, y;
283 284
  GDisplay     *gdisp;
  GimpDrawable *drawable;
285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311

  gdisp = gdisp_ptr;
  drawable = gimage_active_drawable (gdisp->gimage);

  tool->gdisp_ptr = gdisp;

  if (drawable != tool->drawable)
    {
      active_tool->preserve = TRUE;
      image_map_abort (curves_dialog->image_map);
      active_tool->preserve = FALSE;

      tool->drawable = drawable;

      curves_dialog->drawable = drawable;
      curves_dialog->color = drawable_color (drawable);
      curves_dialog->image_map = image_map_create (gdisp, drawable);
    }

  tool->state = ACTIVE;

  gdisplay_untransform_coords (gdisp, bevent->x, bevent->y, &x, &y,
			       FALSE, FALSE);
  curves_colour_update (tool, gdisp, drawable, x, y);
  curves_update (curves_dialog, GRAPH | DRAW);
}

Elliot Lee's avatar
Elliot Lee committed
312 313 314 315 316
static void
curves_button_release (Tool           *tool,
		       GdkEventButton *bevent,
		       gpointer        gdisp_ptr)
{
BST 1999 Andy Thomas's avatar
BST 1999 Andy Thomas committed
317
  gint x, y;
318 319
  GimpDrawable *drawable;
  GDisplay     *gdisp;
BST 1999 Andy Thomas's avatar
BST 1999 Andy Thomas committed
320 321 322 323 324 325 326 327 328

  gdisp = (GDisplay *) gdisp_ptr;

  if(!curves_dialog || 
     !gdisp || 
     !(drawable = gimage_active_drawable (gdisp->gimage)))
     return;

  gdisplay_untransform_coords (gdisp, bevent->x, bevent->y, &x, &y, FALSE, FALSE);
329
  curves_colour_update (tool, gdisp, drawable, x, y);
330

331
  if (bevent->state & GDK_SHIFT_MASK)
332
    {
333 334
      curves_add_point (drawable, x, y, curves_dialog->channel);
      curves_calculate_curve (curves_dialog);
335
    }
336
  else if (bevent->state & GDK_CONTROL_MASK)
337
    {
Michael Natterer's avatar
Michael Natterer committed
338 339 340 341 342
      curves_add_point (drawable, x, y, GIMP_HISTOGRAM_VALUE);
      curves_add_point (drawable, x, y, GIMP_HISTOGRAM_RED);
      curves_add_point (drawable, x, y, GIMP_HISTOGRAM_GREEN);
      curves_add_point (drawable, x, y, GIMP_HISTOGRAM_BLUE);
      curves_add_point (drawable, x, y, GIMP_HISTOGRAM_ALPHA);
343
      curves_calculate_curve (curves_dialog);
344 345 346
    }

  curves_update (curves_dialog, GRAPH | DRAW);
Elliot Lee's avatar
Elliot Lee committed
347 348 349 350 351 352 353
}

static void
curves_motion (Tool           *tool,
	       GdkEventMotion *mevent,
	       gpointer        gdisp_ptr)
{
BST 1999 Andy Thomas's avatar
BST 1999 Andy Thomas committed
354
  gint x, y;
355 356
  GDisplay     *gdisp;
  GimpDrawable *drawable;
BST 1999 Andy Thomas's avatar
BST 1999 Andy Thomas committed
357 358 359 360 361 362 363 364 365

  gdisp = (GDisplay *) gdisp_ptr;

  if(!curves_dialog || 
     !gdisp || 
     !(drawable = gimage_active_drawable (gdisp->gimage)))
     return;

  gdisplay_untransform_coords (gdisp, mevent->x, mevent->y, &x, &y, FALSE, FALSE);
366
  curves_colour_update (tool, gdisp, drawable, x, y);
367
  curves_update (curves_dialog, GRAPH | DRAW);
Elliot Lee's avatar
Elliot Lee committed
368 369 370
}

static void
Michael Natterer's avatar
Michael Natterer committed
371 372 373
curves_control (Tool       *tool,
		ToolAction  action,
		gpointer    gdisp_ptr)
Elliot Lee's avatar
Elliot Lee committed
374 375 376
{
  switch (action)
    {
377
    case PAUSE:
Elliot Lee's avatar
Elliot Lee committed
378
      break;
Michael Natterer's avatar
Michael Natterer committed
379

380
    case RESUME:
Elliot Lee's avatar
Elliot Lee committed
381
      break;
Michael Natterer's avatar
Michael Natterer committed
382

383
    case HALT:
384
      curves_dialog_hide ();
Elliot Lee's avatar
Elliot Lee committed
385
      break;
Michael Natterer's avatar
Michael Natterer committed
386 387 388

    default:
      break;
Elliot Lee's avatar
Elliot Lee committed
389 390 391 392
    }
}

Tool *
393
tools_new_curves (void)
Elliot Lee's avatar
Elliot Lee committed
394
{
395 396
  Tool   *tool;
  Curves *private;
Elliot Lee's avatar
Elliot Lee committed
397 398 399

  /*  The tool options  */
  if (!curves_options)
400
    {
401
      curves_options = tool_options_new (_("Curves"));
402
      tools_register (CURVES, curves_options);
403
    }
Elliot Lee's avatar
Elliot Lee committed
404

405
  tool = tools_new_tool (CURVES);
406
  private = g_new0 (Curves, 1);
Elliot Lee's avatar
Elliot Lee committed
407

408 409
  tool->scroll_lock = TRUE;   /*  Disallow scrolling  */
  tool->preserve    = FALSE;  /*  Don't preserve on drawable change  */
410

411
  tool->private = (void *) private;
412

413
  tool->button_press_func   = curves_button_press;
Elliot Lee's avatar
Elliot Lee committed
414
  tool->button_release_func = curves_button_release;
415 416
  tool->motion_func         = curves_motion;
  tool->control_func        = curves_control;
417

Elliot Lee's avatar
Elliot Lee committed
418 419 420
  return tool;
}

421 422 423 424 425 426 427
void
curves_dialog_hide (void)
{
  if (curves_dialog)
    curves_cancel_callback (NULL, (gpointer) curves_dialog);
}

Elliot Lee's avatar
Elliot Lee committed
428 429 430
void
tools_free_curves (Tool *tool)
{
431
  Curves *private;
Elliot Lee's avatar
Elliot Lee committed
432

433
  private = (Curves *) tool->private;
Elliot Lee's avatar
Elliot Lee committed
434 435

  /*  Close the color select dialog  */
436
  curves_dialog_hide ();
Elliot Lee's avatar
Elliot Lee committed
437

438
  g_free (private);
Elliot Lee's avatar
Elliot Lee committed
439 440 441
}

void
442
curves_initialize (GDisplay *gdisp)
Elliot Lee's avatar
Elliot Lee committed
443
{
444
  gint i, j;
Elliot Lee's avatar
Elliot Lee committed
445 446 447

  if (drawable_indexed (gimage_active_drawable (gdisp->gimage)))
    {
448
      g_message (_("Curves for indexed drawables cannot be adjusted."));
Elliot Lee's avatar
Elliot Lee committed
449 450 451 452 453
      return;
    }

  /*  The curves dialog  */
  if (!curves_dialog)
454 455
    {
      curves_dialog = curves_dialog_new ();
456 457 458 459 460 461 462 463 464 465 466
    }
     
  /*  Initialize the values  */
  curves_dialog->channel = GIMP_HISTOGRAM_VALUE;
  for (i = 0; i < 5; i++)
    for (j = 0; j < 256; j++)
      curves_dialog->curve[i][j] = j;
  
  for (i = 0; i < 5; i++)
    {
      curves_channel_reset (i);
Elliot Lee's avatar
Elliot Lee committed
467 468
    }

469 470
  curves_dialog->drawable  = gimage_active_drawable (gdisp->gimage);
  curves_dialog->color     = drawable_color (curves_dialog->drawable);
471
  curves_dialog->image_map = image_map_create (gdisp, curves_dialog->drawable);
Elliot Lee's avatar
Elliot Lee committed
472 473

  /* check for alpha channel */
474
  if (drawable_has_alpha (curves_dialog->drawable))
475
    gtk_widget_set_sensitive (channel_items[4], TRUE);
Elliot Lee's avatar
Elliot Lee committed
476
  else 
477
    gtk_widget_set_sensitive (channel_items[4], FALSE);
Elliot Lee's avatar
Elliot Lee committed
478 479 480 481
  
  /*  hide or show the channel menu based on image type  */
  if (curves_dialog->color)
    for (i = 0; i < 4; i++) 
482
       gtk_widget_set_sensitive (channel_items[i], TRUE);
Elliot Lee's avatar
Elliot Lee committed
483 484
  else 
    for (i = 1; i < 4; i++) 
485
      gtk_widget_set_sensitive (channel_items[i], FALSE);
Elliot Lee's avatar
Elliot Lee committed
486 487

  /* set the current selection */
488 489
  gtk_option_menu_set_history (GTK_OPTION_MENU (curves_dialog->channel_menu),
			       curves_dialog->channel);
Elliot Lee's avatar
Elliot Lee committed
490

491 492 493 494
  gimp_lut_setup (curves_dialog->lut, 
		  (GimpLutFunc) curves_lut_func,
                  (void *) curves_dialog, 
		  gimp_drawable_bytes (curves_dialog->drawable));
495

496 497
  if (!GTK_WIDGET_VISIBLE (curves_dialog->shell))
    gtk_widget_show (curves_dialog->shell);
Elliot Lee's avatar
Elliot Lee committed
498 499 500 501 502

  curves_update (curves_dialog, GRAPH | DRAW);
}

void
503
curves_free (void)
Elliot Lee's avatar
Elliot Lee committed
504 505 506 507 508
{
  if (curves_dialog)
    {
      if (curves_dialog->image_map)
	{
509
	  active_tool->preserve = TRUE;
Elliot Lee's avatar
Elliot Lee committed
510
	  image_map_abort (curves_dialog->image_map);
511
	  active_tool->preserve = FALSE;
512

Elliot Lee's avatar
Elliot Lee committed
513 514
	  curves_dialog->image_map = NULL;
	}
515

Elliot Lee's avatar
Elliot Lee committed
516
      if (curves_dialog->pixmap)
517
	gdk_pixmap_unref (curves_dialog->pixmap);
518

Elliot Lee's avatar
Elliot Lee committed
519 520 521 522
      gtk_widget_destroy (curves_dialog->shell);
    }
}

523 524 525
/*******************/
/*  Curves dialog  */
/*******************/
Elliot Lee's avatar
Elliot Lee committed
526

527
static CurvesDialog *
528
curves_dialog_new (void)
Elliot Lee's avatar
Elliot Lee committed
529 530 531 532
{
  CurvesDialog *cd;
  GtkWidget *vbox;
  GtkWidget *hbox;
533
  GtkWidget *hbbox;
Elliot Lee's avatar
Elliot Lee committed
534 535 536 537 538
  GtkWidget *label;
  GtkWidget *frame;
  GtkWidget *toggle;
  GtkWidget *channel_hbox;
  GtkWidget *table;
539
  GtkWidget *button;
540
  gint i, j;
541

542
  cd = g_new (CurvesDialog, 1);
543 544 545 546
  cd->cursor_ind_height = -1;
  cd->cursor_ind_width  = -1;
  cd->preview           = TRUE;
  cd->pixmap            = NULL;
Michael Natterer's avatar
Michael Natterer committed
547
  cd->channel           = GIMP_HISTOGRAM_VALUE;
548 549 550 551

  for (i = 0; i < 5; i++)
    cd->curve_type[i] = SMOOTH;

552 553 554
  for (i = 0; i < 5; i++)
    for (j = 0; j < 256; j++)
      cd->curve[i][j] = j;
BST 1999 Andy Thomas's avatar
BST 1999 Andy Thomas committed
555

556
  for (i = 0; i < (sizeof (cd->col_value) / sizeof (cd->col_value[0])); i++)
BST 1999 Andy Thomas's avatar
BST 1999 Andy Thomas committed
557 558
    cd->col_value[i] = 0;

559
  cd->lut = gimp_lut_new ();
Elliot Lee's avatar
Elliot Lee committed
560 561

  /*  The shell and main vbox  */
562
  cd->shell = gimp_dialog_new (_("Curves"), "curves",
563
			       tools_help_func, tool_info[CURVES].private_tip,
564 565 566 567
			       GTK_WIN_POS_NONE,
			       FALSE, TRUE, FALSE,

			       _("OK"), curves_ok_callback,
568
			       cd, NULL, NULL, TRUE, FALSE,
569
			       _("Reset"), curves_reset_callback,
570
			       cd, NULL, NULL, FALSE, FALSE,
571
			       _("Cancel"), curves_cancel_callback,
572
			       cd, NULL, NULL, FALSE, TRUE,
573 574

			       NULL);
Elliot Lee's avatar
Elliot Lee committed
575

576 577
  vbox = gtk_vbox_new (FALSE, 4);
  gtk_container_set_border_width (GTK_CONTAINER (vbox), 4);
578
  gtk_container_add (GTK_CONTAINER (GTK_DIALOG (cd->shell)->vbox), vbox);
Elliot Lee's avatar
Elliot Lee committed
579 580

  /*  The option menu for selecting channels  */
581
  channel_hbox = gtk_hbox_new (FALSE, 4);
Elliot Lee's avatar
Elliot Lee committed
582 583
  gtk_box_pack_start (GTK_BOX (vbox), channel_hbox, FALSE, FALSE, 0);

584
  label = gtk_label_new (_("Modify Curves for Channel:"));
Elliot Lee's avatar
Elliot Lee committed
585 586
  gtk_box_pack_start (GTK_BOX (channel_hbox), label, FALSE, FALSE, 0);

587 588 589 590
  cd->channel_menu = gimp_option_menu_new2
    (FALSE, curves_channel_callback,
     cd, (gpointer) cd->channel,

Michael Natterer's avatar
Michael Natterer committed
591 592 593 594 595
     _("Value"), (gpointer) GIMP_HISTOGRAM_VALUE, &channel_items[0],
     _("Red"),   (gpointer) GIMP_HISTOGRAM_RED,   &channel_items[1],
     _("Green"), (gpointer) GIMP_HISTOGRAM_GREEN, &channel_items[2],
     _("Blue"),  (gpointer) GIMP_HISTOGRAM_BLUE,  &channel_items[3],
     _("Alpha"), (gpointer) GIMP_HISTOGRAM_ALPHA, &channel_items[4],
596 597 598

     NULL);

Elliot Lee's avatar
Elliot Lee committed
599 600 601 602 603 604 605 606
  gtk_box_pack_start (GTK_BOX (channel_hbox), cd->channel_menu, FALSE, FALSE, 2);

  gtk_widget_show (label);
  gtk_widget_show (cd->channel_menu);
  gtk_widget_show (channel_hbox);

  /*  The table for the yrange and the graph  */
  table = gtk_table_new (2, 2, FALSE);
607 608
  gtk_table_set_col_spacings (GTK_TABLE (table), 2);
  gtk_table_set_row_spacings (GTK_TABLE (table), 2);
Elliot Lee's avatar
Elliot Lee committed
609 610 611 612 613 614 615 616
  gtk_box_pack_start (GTK_BOX (vbox), table, FALSE, FALSE, 0);

  /*  The range drawing area  */
  frame = gtk_frame_new (NULL);
  gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN);
  gtk_table_attach (GTK_TABLE (table), frame, 0, 1, 0, 1,
		    GTK_EXPAND, GTK_EXPAND, 0, 0);

617
  cd->yrange = gtk_preview_new (GTK_PREVIEW_COLOR);
Elliot Lee's avatar
Elliot Lee committed
618 619
  gtk_preview_size (GTK_PREVIEW (cd->yrange), YRANGE_WIDTH, YRANGE_HEIGHT);
  gtk_widget_set_events (cd->yrange, RANGE_MASK);
620 621
  gtk_container_add (GTK_CONTAINER (frame), cd->yrange);

Elliot Lee's avatar
Elliot Lee committed
622
  gtk_signal_connect (GTK_OBJECT (cd->yrange), "event",
623
		      GTK_SIGNAL_FUNC (curves_yrange_events),
Elliot Lee's avatar
Elliot Lee committed
624
		      cd);
625

Elliot Lee's avatar
Elliot Lee committed
626 627 628 629 630 631 632
  gtk_widget_show (cd->yrange);
  gtk_widget_show (frame);

  /*  The curves graph  */
  frame = gtk_frame_new (NULL);
  gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN);
  gtk_table_attach (GTK_TABLE (table), frame, 1, 2, 0, 1,
633 634
		    GTK_SHRINK | GTK_FILL,
		    GTK_SHRINK | GTK_FILL, 0, 0);
Elliot Lee's avatar
Elliot Lee committed
635 636 637 638 639 640

  cd->graph = gtk_drawing_area_new ();
  gtk_drawing_area_size (GTK_DRAWING_AREA (cd->graph),
			 GRAPH_WIDTH + RADIUS * 2,
			 GRAPH_HEIGHT + RADIUS * 2);
  gtk_widget_set_events (cd->graph, GRAPH_MASK);
641 642
  gtk_container_add (GTK_CONTAINER (frame), cd->graph);

Elliot Lee's avatar
Elliot Lee committed
643
  gtk_signal_connect (GTK_OBJECT (cd->graph), "event",
644
		      GTK_SIGNAL_FUNC (curves_graph_events),
Elliot Lee's avatar
Elliot Lee committed
645
		      cd);
646

Elliot Lee's avatar
Elliot Lee committed
647 648 649 650 651 652 653 654 655
  gtk_widget_show (cd->graph);
  gtk_widget_show (frame);

  /*  The range drawing area  */
  frame = gtk_frame_new (NULL);
  gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN);
  gtk_table_attach (GTK_TABLE (table), frame, 1, 2, 1, 2,
		    GTK_EXPAND, GTK_EXPAND, 0, 0);

656
  cd->xrange = gtk_preview_new (GTK_PREVIEW_COLOR);
Elliot Lee's avatar
Elliot Lee committed
657 658
  gtk_preview_size (GTK_PREVIEW (cd->xrange), XRANGE_WIDTH, XRANGE_HEIGHT);
  gtk_widget_set_events (cd->xrange, RANGE_MASK);
659 660
  gtk_container_add (GTK_CONTAINER (frame), cd->xrange);

Elliot Lee's avatar
Elliot Lee committed
661
  gtk_signal_connect (GTK_OBJECT (cd->xrange), "event",
662
		      GTK_SIGNAL_FUNC (curves_xrange_events),
Elliot Lee's avatar
Elliot Lee committed
663
		      cd);
664

Elliot Lee's avatar
Elliot Lee committed
665 666 667 668 669
  gtk_widget_show (cd->xrange);
  gtk_widget_show (frame);
  gtk_widget_show (table);

  /*  Horizontal box for preview  */
670
  hbox = gtk_hbox_new (FALSE, 4);
671
  gtk_box_pack_end (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
Elliot Lee's avatar
Elliot Lee committed
672 673

  /*  The option menu for selecting the drawing method  */
674
  label = gtk_label_new (_("Curve Type:"));
Elliot Lee's avatar
Elliot Lee committed
675
  gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
676
  gtk_widget_show (label);
Elliot Lee's avatar
Elliot Lee committed
677

678 679
  cd->curve_type_menu = gimp_option_menu_new
    (FALSE,
Elliot Lee's avatar
Elliot Lee committed
680

681 682 683 684 685
     _("Smooth"), curves_smooth_callback, cd, NULL, NULL, TRUE,
     _("Free"),   curves_free_callback,   cd, NULL, NULL, FALSE,

     NULL);
  gtk_box_pack_start (GTK_BOX (hbox), cd->curve_type_menu, FALSE, FALSE, 2);
686
  gtk_widget_show (cd->curve_type_menu);
687

Elliot Lee's avatar
Elliot Lee committed
688 689 690
  gtk_widget_show (hbox);

  /*  The preview toggle  */
691
  toggle = gtk_check_button_new_with_label (_("Preview"));
692
  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), cd->preview);
693 694
  gtk_box_pack_end (GTK_BOX (hbox), toggle, FALSE, FALSE, 0);

Elliot Lee's avatar
Elliot Lee committed
695
  gtk_signal_connect (GTK_OBJECT (toggle), "toggled",
696
		      GTK_SIGNAL_FUNC (curves_preview_update),
Elliot Lee's avatar
Elliot Lee committed
697 698 699 700 701
		      cd);

  gtk_widget_show (toggle);
  gtk_widget_show (hbox);

702 703 704 705 706 707 708 709 710 711 712
  /*  Horizontal button box for load / save  */
  hbbox = gtk_hbutton_box_new ();
  gtk_button_box_set_spacing (GTK_BUTTON_BOX (hbbox), 4);
  gtk_button_box_set_layout (GTK_BUTTON_BOX (hbbox), GTK_BUTTONBOX_SPREAD);
  gtk_box_pack_end (GTK_BOX (vbox), hbbox, FALSE, FALSE, 0);

  button = gtk_button_new_with_label (_("Load"));
  GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
  gtk_box_pack_start (GTK_BOX (hbbox), button, FALSE, FALSE, 0);
  gtk_signal_connect (GTK_OBJECT (button), "clicked",
		      GTK_SIGNAL_FUNC (curves_load_callback),
713
		      cd->shell);
714 715 716 717 718 719 720
  gtk_widget_show (button);

  button = gtk_button_new_with_label (_("Save"));
  GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
  gtk_box_pack_start (GTK_BOX (hbbox), button, FALSE, FALSE, 0);
  gtk_signal_connect (GTK_OBJECT (button), "clicked",
		      GTK_SIGNAL_FUNC (curves_save_callback),
721
		      cd->shell);
722 723 724
  gtk_widget_show (button);

  gtk_widget_show (hbbox);
Elliot Lee's avatar
Elliot Lee committed
725 726 727 728 729
  gtk_widget_show (vbox);

  return cd;
}

730
static void
731 732 733
curve_print_loc (CurvesDialog *cd,
		 gint          xpos,
		 gint          ypos)
734
{
735 736 737 738
  gchar buf[32];
  gint  width;
  gint  ascent;
  gint  descent;
739

740
  if (cd->cursor_ind_width < 0)
741 742
    {
      /* Calc max extents */
743 744 745 746 747 748 749
      gdk_string_extents (cd->graph->style->font,
			  "x:888 y:888",
			  NULL,
			  NULL,
			  &width,
			  &ascent,
			  &descent);
750 751 752 753 754 755
	
      cd->cursor_ind_width = width;
      cd->cursor_ind_height = ascent + descent;
      cd->cursor_ind_ascent = ascent;
    }
  
756
  if (xpos >= 0 && xpos <= 255 && ypos >=0 && ypos <= 255)
757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780
    {
      g_snprintf (buf, sizeof (buf), "x:%d y:%d",xpos,ypos);

      gdk_draw_rectangle (cd->graph->window, 
			  cd->graph->style->bg_gc[GTK_STATE_ACTIVE],
			  TRUE, RADIUS*2 + 2 , RADIUS*2 + 2, 
			  cd->cursor_ind_width+4, 
			  cd->cursor_ind_height+5);

      gdk_draw_rectangle (cd->graph->window, 
			  cd->graph->style->black_gc,
			  FALSE, RADIUS*2 + 2 , RADIUS*2 + 2, 
			  cd->cursor_ind_width+3, 
			  cd->cursor_ind_height+4);

      gdk_draw_string (cd->graph->window,
		       cd->graph->style->font,
		       cd->graph->style->black_gc,
		       RADIUS*2 + 4,
		       RADIUS*2 + 5 + cd->cursor_ind_ascent,
		       buf);
    }
}

781
/* TODO: preview alpha channel stuff correctly.  -- austin, 20/May/99 */
Elliot Lee's avatar
Elliot Lee committed
782 783
static void
curves_update (CurvesDialog *cd,
784
	       gint          update)
Elliot Lee's avatar
Elliot Lee committed
785 786
{
  GdkRectangle area;
787 788 789
  gint   i, j;
  gchar  buf[32];
  gint   offset;
790 791 792 793 794 795
  gint   sel_channel;
  
  if(cd->color) {
    sel_channel = cd->channel;
  } else {
    if(cd->channel == 2)
Michael Natterer's avatar
Michael Natterer committed
796
      sel_channel = GIMP_HISTOGRAM_ALPHA;
797
    else
Michael Natterer's avatar
Michael Natterer committed
798
      sel_channel = GIMP_HISTOGRAM_VALUE;
799 800
  }

Elliot Lee's avatar
Elliot Lee committed
801 802 803

  if (update & XRANGE_TOP)
    {
804
      guchar buf[XRANGE_WIDTH * 3];
805

806
      switch (sel_channel)
807
	{
Michael Natterer's avatar
Michael Natterer committed
808 809
	case GIMP_HISTOGRAM_VALUE:
	case GIMP_HISTOGRAM_ALPHA:
810
	  for (i = 0; i < XRANGE_HEIGHT / 2; i++)
811
	    {
812
	      for (j = 0; j < XRANGE_WIDTH ; j++)
813
		{
814 815 816
		  buf[j*3+0] = cd->curve[sel_channel][j];
		  buf[j*3+1] = cd->curve[sel_channel][j];
		  buf[j*3+2] = cd->curve[sel_channel][j];
817
		}
818 819
	      gtk_preview_draw_row (GTK_PREVIEW (cd->xrange),
				    buf, 0, i, XRANGE_WIDTH);
820
	    }
821 822
	  break;

Michael Natterer's avatar
Michael Natterer committed
823 824 825
	case GIMP_HISTOGRAM_RED:
	case GIMP_HISTOGRAM_GREEN:
	case GIMP_HISTOGRAM_BLUE:
826
	  {
827
	    for (i = 0; i < XRANGE_HEIGHT / 2; i++)
828
	      {
829 830
		for (j = 0; j < XRANGE_WIDTH; j++)
		  {
Michael Natterer's avatar
Michael Natterer committed
831 832 833
		    buf[j*3+0] = cd->curve[GIMP_HISTOGRAM_RED][j];
		    buf[j*3+1] = cd->curve[GIMP_HISTOGRAM_GREEN][j];
		    buf[j*3+2] = cd->curve[GIMP_HISTOGRAM_BLUE][j];
834 835 836
		  }
		gtk_preview_draw_row (GTK_PREVIEW (cd->xrange),
				      buf, 0, i, XRANGE_WIDTH);
837
	      }
838
	    break;
839 840
	  }

841
	default:
842 843 844
	  g_warning ("unknown channel type %d, can't happen!?!?",
		     cd->channel);
	  break;
845
	}
Elliot Lee's avatar
Elliot Lee committed
846 847 848 849 850 851 852 853 854 855 856 857

      if (update & DRAW)
	{
	  area.x = 0;
	  area.y = 0;
	  area.width = XRANGE_WIDTH;
	  area.height = XRANGE_HEIGHT / 2;
	  gtk_widget_draw (cd->xrange, &area);
	}
    }
  if (update & XRANGE_BOTTOM)
    {
858
      guchar buf[XRANGE_WIDTH * 3];
Elliot Lee's avatar
Elliot Lee committed
859 860

      for (i = 0; i < XRANGE_WIDTH; i++)
861 862 863 864 865
      {
	buf[i*3+0] = i;
	buf[i*3+1] = i;
	buf[i*3+2] = i;
      }
Elliot Lee's avatar
Elliot Lee committed
866 867 868 869 870 871 872 873 874 875 876 877 878 879 880

      for (i = XRANGE_HEIGHT / 2; i < XRANGE_HEIGHT; i++)
	gtk_preview_draw_row (GTK_PREVIEW (cd->xrange), buf, 0, i, XRANGE_WIDTH);

      if (update & DRAW)
	{
	  area.x = 0;
	  area.y = XRANGE_HEIGHT / 2;
	  area.width = XRANGE_WIDTH;
	  area.height = XRANGE_HEIGHT / 2;
	  gtk_widget_draw (cd->xrange, &area);
	}
    }
  if (update & YRANGE)
    {
881 882
      guchar buf[YRANGE_WIDTH * 3];
      guchar pix[3];
Elliot Lee's avatar
Elliot Lee committed
883 884 885

      for (i = 0; i < YRANGE_HEIGHT; i++)
	{
886
	  switch (sel_channel)
887
	    {
Michael Natterer's avatar
Michael Natterer committed
888 889
	    case GIMP_HISTOGRAM_VALUE:
	    case GIMP_HISTOGRAM_ALPHA:
890 891 892
	      pix[0] = pix[1] = pix[2] = (255 - i);
	      break;

Michael Natterer's avatar
Michael Natterer committed
893 894 895
	    case GIMP_HISTOGRAM_RED:
	    case GIMP_HISTOGRAM_GREEN:
	    case GIMP_HISTOGRAM_BLUE:
896
	      pix[0] = pix[1] = pix[2] = 0;
897
	      pix[sel_channel - 1] = (255 - i);
898 899
	      break;

900
	    default:
901 902 903
	      g_warning ("unknown channel type %d, can't happen!?!?",
			 cd->channel);
	      break;
904
	    }
905 906

	  for (j = 0; j < YRANGE_WIDTH * 3; j++)
907
	    buf[j] = pix[j%3];
Elliot Lee's avatar
Elliot Lee committed
908

909 910
	  gtk_preview_draw_row (GTK_PREVIEW (cd->yrange),
				buf, 0, i, YRANGE_WIDTH);
Elliot Lee's avatar
Elliot Lee committed
911 912 913 914 915 916 917 918 919 920 921
	}

      if (update & DRAW)
	gtk_widget_draw (cd->yrange, NULL);
    }
  if ((update & GRAPH) && (update & DRAW) && cd->pixmap != NULL)
    {
      GdkPoint points[256];

      /*  Clear the pixmap  */
      gdk_draw_rectangle (cd->pixmap, cd->graph->style->bg_gc[GTK_STATE_NORMAL],
922 923
			  TRUE, 0, 0,
			  GRAPH_WIDTH + RADIUS * 2, GRAPH_HEIGHT + RADIUS * 2);
Elliot Lee's avatar
Elliot Lee committed
924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944

      /*  Draw the grid lines  */
      for (i = 0; i < 5; i++)
	{
	  gdk_draw_line (cd->pixmap, cd->graph->style->dark_gc[GTK_STATE_NORMAL],
			 RADIUS, i * (GRAPH_HEIGHT / 4) + RADIUS,
			 GRAPH_WIDTH + RADIUS, i * (GRAPH_HEIGHT / 4) + RADIUS);
	  gdk_draw_line (cd->pixmap, cd->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 - cd->curve[cd->channel][i] + RADIUS;
	}
      gdk_draw_points (cd->pixmap, cd->graph->style->black_gc, points, 256);

      /*  Draw the points  */
945
      if (cd->curve_type[cd->channel] == SMOOTH)
Elliot Lee's avatar
Elliot Lee committed
946 947 948 949 950 951 952 953 954
	for (i = 0; i < 17; i++)
	  {
	    if (cd->points[cd->channel][i][0] != -1)
	      gdk_draw_arc (cd->pixmap, cd->graph->style->black_gc, TRUE,
			    cd->points[cd->channel][i][0],
			    255 - cd->points[cd->channel][i][1],
			    RADIUS * 2, RADIUS * 2, 0, 23040);
	  }

BST 1999 Andy Thomas's avatar
BST 1999 Andy Thomas committed
955
      /* draw the colour line */
956
      gdk_draw_line (cd->pixmap, cd->graph->style->black_gc,
957 958
		     cd->col_value[sel_channel]+RADIUS,RADIUS,
		     cd->col_value[sel_channel]+RADIUS,GRAPH_HEIGHT + RADIUS);
BST 1999 Andy Thomas's avatar
BST 1999 Andy Thomas committed
959

960
      /* and xpos indicator */
961
      g_snprintf (buf, sizeof (buf), "x:%d",cd->col_value[sel_channel]);
962
      
963
      if ((cd->col_value[sel_channel]+RADIUS) < 127)
964 965 966 967 968
	{
	  offset = RADIUS + 4;
	}
      else
	{
969
	  offset = - gdk_string_width (cd->graph->style->font,buf) - 2;
970 971 972 973 974
	}

      gdk_draw_string (cd->pixmap,
		       cd->graph->style->font,
		       cd->graph->style->black_gc,
975
		       cd->col_value[sel_channel]+offset,
976 977 978
		       GRAPH_HEIGHT,
		       buf);

Elliot Lee's avatar
Elliot Lee committed
979 980
      gdk_draw_pixmap (cd->graph->window, cd->graph->style->black_gc, cd->pixmap,
		       0, 0, 0, 0, GRAPH_WIDTH + RADIUS * 2, GRAPH_HEIGHT + RADIUS * 2);
981
      
Elliot Lee's avatar
Elliot Lee committed
982 983 984 985 986
    }
}

static void
curves_plot_curve (CurvesDialog *cd,
987 988 989 990
		   gint          p1,
		   gint          p2,
		   gint          p3,
		   gint          p4)
Elliot Lee's avatar
Elliot Lee committed
991 992 993 994
{
  CRMatrix geometry;
  CRMatrix tmp1, tmp2;
  CRMatrix deltas;
995 996 997 998 999