curves.c 44 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
#include <gtk/gtk.h>

25
#include "libgimpmath/gimpmath.h"
26
#include "libgimpwidgets/gimpwidgets.h"
27

Sven Neumann's avatar
Sven Neumann committed
28 29
#include "apptypes.h"

Elliot Lee's avatar
Elliot Lee committed
30 31 32 33
#include "appenv.h"
#include "cursorutil.h"
#include "drawable.h"
#include "gdisplay.h"
34
#include "gimphistogram.h"
35
#include "gimpimage.h"
36
#include "gimpui.h"
37
#include "gimplut.h"
38
#include "image_map.h"
39 40

#include "curves.h"
41
#include "tool_options.h"
42
#include "tools.h"
Elliot Lee's avatar
Elliot Lee committed
43

44
#include "libgimp/gimpenv.h"
45

46 47
#include "libgimp/gimpintl.h"

48

49 50 51 52 53 54
#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
55

56 57 58
/*  NB: take care when changing these values: make sure the curve[] array in
 *  curves.h is large enough.
 */
59 60 61 62 63 64
#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
65 66 67 68 69 70 71 72 73 74 75 76 77 78
#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

79
/*  the curves structures  */
Elliot Lee's avatar
Elliot Lee committed
80

81
typedef struct _Curves Curves;
82

Elliot Lee's avatar
Elliot Lee committed
83 84
struct _Curves
{
85
  gint x, y;    /*  coords for last mouse click  */
Elliot Lee's avatar
Elliot Lee committed
86 87
};

88
typedef gdouble CRMatrix[4][4];
89

90
/*  the curves tool options  */
91
static ToolOptions  * curves_options = NULL;
92 93

/*  the curves dialog  */
94
static CurvesDialog * curves_dialog = NULL;
95

96 97 98 99
/*  the curves file dialog  */
static GtkWidget *file_dlg = NULL;
static gboolean   load_save;

100 101
static GtkWidget *channel_items[5];

102 103 104 105 106 107 108 109
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
110

111
/*  curves action functions  */
Michael Natterer's avatar
Michael Natterer committed
112

113 114 115 116 117 118 119 120 121 122 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 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178
static void   curves_button_press         (Tool           *tool,
					   GdkEventButton *bevent,
					   GDisplay       *gdisp);
static void   curves_button_release       (Tool           *tool,
					   GdkEventButton *bevent,
					   GDisplay       *gdisp);
static void   curves_motion               (Tool           *tool,
					   GdkEventMotion *mevent,
					   GDisplay       *gdisp);
static void   curves_control              (Tool           *tool,
					   ToolAction      action,
					   GDisplay       *gdisp);

static CurvesDialog * curves_dialog_new   (void);

static void   curves_update               (CurvesDialog   *cd,
					   gint            );
static void   curves_plot_curve           (CurvesDialog   *cd,
					   gint            ,
					   gint            ,
					   gint            ,
					   gint            );
static void   curves_preview              (CurvesDialog   *cd);

static void   curves_channel_callback     (GtkWidget      *widget,
					   gpointer        data);

static void   curves_smooth_callback      (GtkWidget      *widget,
					   gpointer        data);
static void   curves_free_callback        (GtkWidget      *widget,
					   gpointer        data);

static void   curves_channel_reset        (gint            );
static void   curves_reset_callback       (GtkWidget      *widget,
					   gpointer        data);
static void   curves_ok_callback          (GtkWidget      *widget,
					   gpointer        data);
static void   curves_cancel_callback      (GtkWidget      *widget,
					   gpointer        data);
static void   curves_load_callback        (GtkWidget      *widget,
					   gpointer        data);
static void   curves_save_callback        (GtkWidget      *widget,
					   gpointer        data);
static void   curves_preview_update       (GtkWidget      *widget,
					   gpointer        data);
static gint   curves_xrange_events        (GtkWidget      *widget,
					   GdkEvent       *event,
					   CurvesDialog   *cd);
static gint   curves_yrange_events        (GtkWidget      *widget,
					   GdkEvent       *event,
					   CurvesDialog   *cd);
static gint   curves_graph_events         (GtkWidget      *widget,
					   GdkEvent       *event,
					   CurvesDialog   *cd);
static void   curves_CR_compose           (CRMatrix        ,
					   CRMatrix        ,
					   CRMatrix        );

static void   file_dialog_create          (GtkWidget      *widget);
static void   file_dialog_ok_callback     (GtkWidget      *widget,
					   gpointer        data);
static void   file_dialog_cancel_callback (GtkWidget      *widget,
					   gpointer        data);

static gboolean  curves_read_from_file    (FILE           *f);
static void      curves_write_to_file     (FILE           *f);
179 180


Elliot Lee's avatar
Elliot Lee committed
181 182
/*  curves machinery  */

183
gfloat
Manish Singh's avatar
Manish Singh committed
184
curves_lut_func (CurvesDialog *cd,
185 186 187
		 gint          nchannels,
		 gint          channel,
		 gfloat        value)
Elliot Lee's avatar
Elliot Lee committed
188
{
189 190
  gfloat  f;
  gint    index;
191
  gdouble inten;
192
  gint    j;
Elliot Lee's avatar
Elliot Lee committed
193

194 195 196 197
  if (nchannels == 1)
    j = 0;
  else
    j = channel + 1;
198

199
  inten = value;
200

Manish Singh's avatar
Manish Singh committed
201
  /* For color images this runs through the loop with j = channel +1
202 203 204 205 206 207 208 209 210 211 212 213 214 215 216
     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
217
    {
218 219 220 221
      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
222
    }
223 224
  }
  return inten;
Elliot Lee's avatar
Elliot Lee committed
225 226
}

BST 1999 Andy Thomas's avatar
BST 1999 Andy Thomas committed
227
static void
Michael Natterer's avatar
Michael Natterer committed
228 229 230 231 232
curves_colour_update (Tool           *tool,
		      GDisplay       *gdisp,
		      GimpDrawable   *drawable,
		      gint            x,
		      gint            y)
BST 1999 Andy Thomas's avatar
BST 1999 Andy Thomas committed
233
{
234 235 236 237
  guchar        *color;
  gint           offx;
  gint           offy;
  gint           maxval;
238 239 240
  gboolean       has_alpha;
  gboolean       is_indexed;
  GimpImageType  sample_type;
BST 1999 Andy Thomas's avatar
BST 1999 Andy Thomas committed
241

242
  if (!tool || tool->state != ACTIVE)
BST 1999 Andy Thomas's avatar
BST 1999 Andy Thomas committed
243 244
    return;

245
  gimp_drawable_offsets (drawable, &offx, &offy);
BST 1999 Andy Thomas's avatar
BST 1999 Andy Thomas committed
246 247 248 249

  x -= offx;
  y -= offy;

250
  if (!(color = image_map_get_color_at (curves_dialog->image_map, x, y)))
BST 1999 Andy Thomas's avatar
BST 1999 Andy Thomas committed
251 252
    return;

253
  sample_type = gimp_drawable_type (drawable);
254 255
  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
256

Michael Natterer's avatar
Michael Natterer committed
257 258 259
  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
260 261 262

  if (has_alpha)
    {
Michael Natterer's avatar
Michael Natterer committed
263
      curves_dialog->col_value [GIMP_HISTOGRAM_ALPHA] = color[3];
BST 1999 Andy Thomas's avatar
BST 1999 Andy Thomas committed
264 265 266
    }

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

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

272
  g_free (color);
273
}
BST 1999 Andy Thomas's avatar
BST 1999 Andy Thomas committed
274

275
static void
276 277 278 279
curves_add_point (GimpDrawable *drawable,
		  gint          x,
		  gint          y,
		  gint          cchan)
280 281
{
  /* Add point onto the curve */
282 283 284 285
  gint closest_point = 0;
  gint distance;
  gint curvex;
  gint i;
286

287
  switch (curves_dialog->curve_type[cchan])
288
    {
289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311
    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;
312
    }
Elliot Lee's avatar
Elliot Lee committed
313 314
}

315 316 317 318 319
/*  curves action functions  */

static void
curves_button_press (Tool           *tool,
		     GdkEventButton *bevent,
320
		     GDisplay       *gdisp)
321
{
322
  gint          x, y;
323
  GimpDrawable *drawable;
324

325
  drawable = gimp_image_active_drawable (gdisp->gimage);
326

327
  tool->gdisp = gdisp;
328 329 330 331 332 333 334 335 336 337

  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;
338
      curves_dialog->color = gimp_drawable_is_rgb (drawable);
339 340 341 342 343 344 345 346 347 348 349
      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
350 351 352
static void
curves_button_release (Tool           *tool,
		       GdkEventButton *bevent,
353
		       GDisplay       *gdisp)
Elliot Lee's avatar
Elliot Lee committed
354
{
355
  gint          x, y;
356
  GimpDrawable *drawable;
BST 1999 Andy Thomas's avatar
BST 1999 Andy Thomas committed
357

358 359 360
  if (! curves_dialog || 
      ! gdisp || 
      ! (drawable = gimp_image_active_drawable (gdisp->gimage)))
BST 1999 Andy Thomas's avatar
BST 1999 Andy Thomas committed
361 362
     return;

363 364
  gdisplay_untransform_coords (gdisp, bevent->x, bevent->y, &x, &y,
			       FALSE, FALSE);
365
  curves_colour_update (tool, gdisp, drawable, x, y);
366

367
  if (bevent->state & GDK_SHIFT_MASK)
368
    {
369 370
      curves_add_point (drawable, x, y, curves_dialog->channel);
      curves_calculate_curve (curves_dialog);
371
    }
372
  else if (bevent->state & GDK_CONTROL_MASK)
373
    {
Michael Natterer's avatar
Michael Natterer committed
374 375 376 377 378
      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);
379
      curves_calculate_curve (curves_dialog);
380 381 382
    }

  curves_update (curves_dialog, GRAPH | DRAW);
Elliot Lee's avatar
Elliot Lee committed
383 384 385 386 387
}

static void
curves_motion (Tool           *tool,
	       GdkEventMotion *mevent,
388
	       GDisplay       *gdisp)
Elliot Lee's avatar
Elliot Lee committed
389
{
390
  gint          x, y;
391
  GimpDrawable *drawable;
BST 1999 Andy Thomas's avatar
BST 1999 Andy Thomas committed
392

393 394 395
  if (! curves_dialog ||
      ! gdisp || 
      ! (drawable = gimp_image_active_drawable (gdisp->gimage)))
BST 1999 Andy Thomas's avatar
BST 1999 Andy Thomas committed
396 397 398
     return;

  gdisplay_untransform_coords (gdisp, mevent->x, mevent->y, &x, &y, FALSE, FALSE);
399
  curves_colour_update (tool, gdisp, drawable, x, y);
400
  curves_update (curves_dialog, GRAPH | DRAW);
Elliot Lee's avatar
Elliot Lee committed
401 402 403
}

static void
Michael Natterer's avatar
Michael Natterer committed
404 405
curves_control (Tool       *tool,
		ToolAction  action,
406
		GDisplay   *gdisp)
Elliot Lee's avatar
Elliot Lee committed
407 408 409
{
  switch (action)
    {
410
    case PAUSE:
Elliot Lee's avatar
Elliot Lee committed
411
      break;
Michael Natterer's avatar
Michael Natterer committed
412

413
    case RESUME:
Elliot Lee's avatar
Elliot Lee committed
414
      break;
Michael Natterer's avatar
Michael Natterer committed
415

416
    case HALT:
417
      curves_dialog_hide ();
Elliot Lee's avatar
Elliot Lee committed
418
      break;
Michael Natterer's avatar
Michael Natterer committed
419 420 421

    default:
      break;
Elliot Lee's avatar
Elliot Lee committed
422 423 424 425
    }
}

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

  /*  The tool options  */
  if (!curves_options)
433
    {
434
      curves_options = tool_options_new (_("Curves"));
435
      tools_register (CURVES, curves_options);
436
    }
Elliot Lee's avatar
Elliot Lee committed
437

438
  tool = tools_new_tool (CURVES);
439
  private = g_new0 (Curves, 1);
Elliot Lee's avatar
Elliot Lee committed
440

441 442
  tool->scroll_lock = TRUE;   /*  Disallow scrolling  */
  tool->preserve    = FALSE;  /*  Don't preserve on drawable change  */
443

444
  tool->private = (void *) private;
445

446
  tool->button_press_func   = curves_button_press;
Elliot Lee's avatar
Elliot Lee committed
447
  tool->button_release_func = curves_button_release;
448 449
  tool->motion_func         = curves_motion;
  tool->control_func        = curves_control;
450

Elliot Lee's avatar
Elliot Lee committed
451 452 453
  return tool;
}

454 455 456 457 458 459 460
void
curves_dialog_hide (void)
{
  if (curves_dialog)
    curves_cancel_callback (NULL, (gpointer) curves_dialog);
}

Elliot Lee's avatar
Elliot Lee committed
461 462 463
void
tools_free_curves (Tool *tool)
{
464
  Curves *private;
Elliot Lee's avatar
Elliot Lee committed
465

466
  private = (Curves *) tool->private;
Elliot Lee's avatar
Elliot Lee committed
467 468

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

471
  g_free (private);
Elliot Lee's avatar
Elliot Lee committed
472 473 474
}

void
475
curves_initialize (GDisplay *gdisp)
Elliot Lee's avatar
Elliot Lee committed
476
{
477
  gint i, j;
Elliot Lee's avatar
Elliot Lee committed
478

479
  if (gimp_drawable_is_indexed (gimp_image_active_drawable (gdisp->gimage)))
Elliot Lee's avatar
Elliot Lee committed
480
    {
481
      g_message (_("Curves for indexed drawables cannot be adjusted."));
Elliot Lee's avatar
Elliot Lee committed
482 483 484 485 486
      return;
    }

  /*  The curves dialog  */
  if (!curves_dialog)
487 488
    {
      curves_dialog = curves_dialog_new ();
489 490 491 492 493 494 495 496 497 498 499
    }
     
  /*  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
500 501
    }

502
  curves_dialog->drawable  = gimp_image_active_drawable (gdisp->gimage);
503
  curves_dialog->color     = gimp_drawable_is_rgb (curves_dialog->drawable);
504
  curves_dialog->image_map = image_map_create (gdisp, curves_dialog->drawable);
Elliot Lee's avatar
Elliot Lee committed
505 506

  /* check for alpha channel */
507
  if (gimp_drawable_has_alpha (curves_dialog->drawable))
508
    gtk_widget_set_sensitive (channel_items[4], TRUE);
Elliot Lee's avatar
Elliot Lee committed
509
  else 
510
    gtk_widget_set_sensitive (channel_items[4], FALSE);
Elliot Lee's avatar
Elliot Lee committed
511 512 513 514
  
  /*  hide or show the channel menu based on image type  */
  if (curves_dialog->color)
    for (i = 0; i < 4; i++) 
515
       gtk_widget_set_sensitive (channel_items[i], TRUE);
Elliot Lee's avatar
Elliot Lee committed
516 517
  else 
    for (i = 1; i < 4; i++) 
518
      gtk_widget_set_sensitive (channel_items[i], FALSE);
Elliot Lee's avatar
Elliot Lee committed
519 520

  /* set the current selection */
521 522
  gtk_option_menu_set_history (GTK_OPTION_MENU (curves_dialog->channel_menu),
			       curves_dialog->channel);
Elliot Lee's avatar
Elliot Lee committed
523

524 525 526 527
  gimp_lut_setup (curves_dialog->lut, 
		  (GimpLutFunc) curves_lut_func,
                  (void *) curves_dialog, 
		  gimp_drawable_bytes (curves_dialog->drawable));
528

529 530
  if (!GTK_WIDGET_VISIBLE (curves_dialog->shell))
    gtk_widget_show (curves_dialog->shell);
Elliot Lee's avatar
Elliot Lee committed
531 532 533 534 535

  curves_update (curves_dialog, GRAPH | DRAW);
}

void
536
curves_free (void)
Elliot Lee's avatar
Elliot Lee committed
537 538 539 540 541
{
  if (curves_dialog)
    {
      if (curves_dialog->image_map)
	{
542
	  active_tool->preserve = TRUE;
Elliot Lee's avatar
Elliot Lee committed
543
	  image_map_abort (curves_dialog->image_map);
544
	  active_tool->preserve = FALSE;
545

Elliot Lee's avatar
Elliot Lee committed
546 547
	  curves_dialog->image_map = NULL;
	}
548

Elliot Lee's avatar
Elliot Lee committed
549
      if (curves_dialog->pixmap)
550
	gdk_pixmap_unref (curves_dialog->pixmap);
551

Elliot Lee's avatar
Elliot Lee committed
552 553 554 555
      gtk_widget_destroy (curves_dialog->shell);
    }
}

556 557 558
/*******************/
/*  Curves dialog  */
/*******************/
Elliot Lee's avatar
Elliot Lee committed
559

560
static CurvesDialog *
561
curves_dialog_new (void)
Elliot Lee's avatar
Elliot Lee committed
562 563 564 565
{
  CurvesDialog *cd;
  GtkWidget *vbox;
  GtkWidget *hbox;
566
  GtkWidget *hbbox;
Elliot Lee's avatar
Elliot Lee committed
567 568 569 570 571
  GtkWidget *label;
  GtkWidget *frame;
  GtkWidget *toggle;
  GtkWidget *channel_hbox;
  GtkWidget *table;
572
  GtkWidget *button;
573
  gint i, j;
574

575
  cd = g_new (CurvesDialog, 1);
576 577 578 579
  cd->cursor_ind_height = -1;
  cd->cursor_ind_width  = -1;
  cd->preview           = TRUE;
  cd->pixmap            = NULL;
Michael Natterer's avatar
Michael Natterer committed
580
  cd->channel           = GIMP_HISTOGRAM_VALUE;
581 582 583 584

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

585 586 587
  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
588

589
  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
590 591
    cd->col_value[i] = 0;

592
  cd->lut = gimp_lut_new ();
Elliot Lee's avatar
Elliot Lee committed
593 594

  /*  The shell and main vbox  */
595
  cd->shell = gimp_dialog_new (_("Curves"), "curves",
596
			       tools_help_func, tool_info[CURVES].private_tip,
597 598 599 600
			       GTK_WIN_POS_NONE,
			       FALSE, TRUE, FALSE,

			       _("OK"), curves_ok_callback,
601
			       cd, NULL, NULL, TRUE, FALSE,
602
			       _("Reset"), curves_reset_callback,
603
			       cd, NULL, NULL, FALSE, FALSE,
604
			       _("Cancel"), curves_cancel_callback,
605
			       cd, NULL, NULL, FALSE, TRUE,
606 607

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

609 610
  vbox = gtk_vbox_new (FALSE, 4);
  gtk_container_set_border_width (GTK_CONTAINER (vbox), 4);
611
  gtk_container_add (GTK_CONTAINER (GTK_DIALOG (cd->shell)->vbox), vbox);
Elliot Lee's avatar
Elliot Lee committed
612 613

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

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

620 621 622 623
  cd->channel_menu = gimp_option_menu_new2
    (FALSE, curves_channel_callback,
     cd, (gpointer) cd->channel,

Michael Natterer's avatar
Michael Natterer committed
624 625 626 627 628
     _("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],
629 630 631

     NULL);

Elliot Lee's avatar
Elliot Lee committed
632 633 634 635 636 637 638 639
  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);
640 641
  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
642 643 644 645 646 647 648 649
  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);

650
  cd->yrange = gtk_preview_new (GTK_PREVIEW_COLOR);
Elliot Lee's avatar
Elliot Lee committed
651 652
  gtk_preview_size (GTK_PREVIEW (cd->yrange), YRANGE_WIDTH, YRANGE_HEIGHT);
  gtk_widget_set_events (cd->yrange, RANGE_MASK);
653 654
  gtk_container_add (GTK_CONTAINER (frame), cd->yrange);

Elliot Lee's avatar
Elliot Lee committed
655
  gtk_signal_connect (GTK_OBJECT (cd->yrange), "event",
656
		      GTK_SIGNAL_FUNC (curves_yrange_events),
Elliot Lee's avatar
Elliot Lee committed
657
		      cd);
658

Elliot Lee's avatar
Elliot Lee committed
659 660 661 662 663 664 665
  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,
666 667
		    GTK_SHRINK | GTK_FILL,
		    GTK_SHRINK | GTK_FILL, 0, 0);
Elliot Lee's avatar
Elliot Lee committed
668 669 670 671 672 673

  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);
674 675
  gtk_container_add (GTK_CONTAINER (frame), cd->graph);

Elliot Lee's avatar
Elliot Lee committed
676
  gtk_signal_connect (GTK_OBJECT (cd->graph), "event",
677
		      GTK_SIGNAL_FUNC (curves_graph_events),
Elliot Lee's avatar
Elliot Lee committed
678
		      cd);
679

Elliot Lee's avatar
Elliot Lee committed
680 681 682 683 684 685 686 687 688
  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);

689
  cd->xrange = gtk_preview_new (GTK_PREVIEW_COLOR);
Elliot Lee's avatar
Elliot Lee committed
690 691
  gtk_preview_size (GTK_PREVIEW (cd->xrange), XRANGE_WIDTH, XRANGE_HEIGHT);
  gtk_widget_set_events (cd->xrange, RANGE_MASK);
692 693
  gtk_container_add (GTK_CONTAINER (frame), cd->xrange);

Elliot Lee's avatar
Elliot Lee committed
694
  gtk_signal_connect (GTK_OBJECT (cd->xrange), "event",
695
		      GTK_SIGNAL_FUNC (curves_xrange_events),
Elliot Lee's avatar
Elliot Lee committed
696
		      cd);
697

Elliot Lee's avatar
Elliot Lee committed
698 699 700 701 702
  gtk_widget_show (cd->xrange);
  gtk_widget_show (frame);
  gtk_widget_show (table);

  /*  Horizontal box for preview  */
703
  hbox = gtk_hbox_new (FALSE, 4);
704
  gtk_box_pack_end (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
Elliot Lee's avatar
Elliot Lee committed
705 706

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

711 712
  cd->curve_type_menu = gimp_option_menu_new
    (FALSE,
Elliot Lee's avatar
Elliot Lee committed
713

714 715 716 717 718
     _("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);
719
  gtk_widget_show (cd->curve_type_menu);
720

Elliot Lee's avatar
Elliot Lee committed
721 722 723
  gtk_widget_show (hbox);

  /*  The preview toggle  */
724
  toggle = gtk_check_button_new_with_label (_("Preview"));
725
  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), cd->preview);
726 727
  gtk_box_pack_end (GTK_BOX (hbox), toggle, FALSE, FALSE, 0);

Elliot Lee's avatar
Elliot Lee committed
728
  gtk_signal_connect (GTK_OBJECT (toggle), "toggled",
729
		      GTK_SIGNAL_FUNC (curves_preview_update),
Elliot Lee's avatar
Elliot Lee committed
730 731 732 733 734
		      cd);

  gtk_widget_show (toggle);
  gtk_widget_show (hbox);

735 736 737 738 739 740 741 742 743 744 745
  /*  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),
746
		      cd->shell);
747 748 749 750 751 752 753
  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),
754
		      cd->shell);
755 756 757
  gtk_widget_show (button);

  gtk_widget_show (hbbox);
Elliot Lee's avatar
Elliot Lee committed
758 759 760 761 762
  gtk_widget_show (vbox);

  return cd;
}

763
static void
764 765 766
curve_print_loc (CurvesDialog *cd,
		 gint          xpos,
		 gint          ypos)
767
{
768 769 770 771
  gchar buf[32];
  gint  width;
  gint  ascent;
  gint  descent;
772

773
  if (cd->cursor_ind_width < 0)
774 775
    {
      /* Calc max extents */
776 777 778 779 780 781 782
      gdk_string_extents (cd->graph->style->font,
			  "x:888 y:888",
			  NULL,
			  NULL,
			  &width,
			  &ascent,
			  &descent);
783 784 785 786 787 788
	
      cd->cursor_ind_width = width;
      cd->cursor_ind_height = ascent + descent;
      cd->cursor_ind_ascent = ascent;
    }
  
789
  if (xpos >= 0 && xpos <= 255 && ypos >=0 && ypos <= 255)
790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813
    {
      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);
    }
}

814
/* TODO: preview alpha channel stuff correctly.  -- austin, 20/May/99 */
Elliot Lee's avatar
Elliot Lee committed
815 816
static void
curves_update (CurvesDialog *cd,
817
	       gint          update)
Elliot Lee's avatar
Elliot Lee committed
818 819
{
  GdkRectangle area;
820 821 822
  gint   i, j;
  gchar  buf[32];
  gint   offset;
823 824 825 826 827 828
  gint   sel_channel;
  
  if(cd->color) {
    sel_channel = cd->channel;
  } else {
    if(cd->channel == 2)
Michael Natterer's avatar
Michael Natterer committed
829
      sel_channel = GIMP_HISTOGRAM_ALPHA;
830
    else
Michael Natterer's avatar
Michael Natterer committed
831
      sel_channel = GIMP_HISTOGRAM_VALUE;
832 833
  }

Elliot Lee's avatar
Elliot Lee committed
834 835 836

  if (update & XRANGE_TOP)
    {
837
      guchar buf[XRANGE_WIDTH * 3];
838

839
      switch (sel_channel)
840
	{
Michael Natterer's avatar
Michael Natterer committed
841 842
	case GIMP_HISTOGRAM_VALUE:
	case GIMP_HISTOGRAM_ALPHA:
843
	  for (i = 0; i < XRANGE_HEIGHT / 2; i++)
844
	    {
845
	      for (j = 0; j < XRANGE_WIDTH ; j++)
846
		{
847 848 849
		  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];
850
		}
851 852
	      gtk_preview_draw_row (GTK_PREVIEW (cd->xrange),
				    buf, 0, i, XRANGE_WIDTH);
853
	    }
854 855
	  break;

Michael Natterer's avatar
Michael Natterer committed
856 857 858
	case GIMP_HISTOGRAM_RED:
	case GIMP_HISTOGRAM_GREEN:
	case GIMP_HISTOGRAM_BLUE:
859
	  {
860
	    for (i = 0; i < XRANGE_HEIGHT / 2; i++)
861
	      {
862 863
		for (j = 0; j < XRANGE_WIDTH; j++)
		  {
Michael Natterer's avatar
Michael Natterer committed
864 865 866
		    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];
867 868 869
		  }
		gtk_preview_draw_row (GTK_PREVIEW (cd->xrange),
				      buf, 0, i, XRANGE_WIDTH);
870
	      }
871
	    break;
872 873
	  }

874
	default:
875 876 877
	  g_warning ("unknown channel type %d, can't happen!?!?",
		     cd->channel);
	  break;
878
	}
Elliot Lee's avatar
Elliot Lee committed
879 880 881 882 883 884 885 886 887 888 889 890

      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)
    {
891
      guchar buf[XRANGE_WIDTH * 3];
Elliot Lee's avatar
Elliot Lee committed
892 893

      for (i = 0; i < XRANGE_WIDTH; i++)
894 895 896 897 898
      {
	buf[i*3+0] = i;
	buf[i*3+1] = i;
	buf[i*3+2] = i;
      }
Elliot Lee's avatar
Elliot Lee committed
899 900 901 902 903 904 905 906 907 908 909 910 911 912 913

      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)
    {
914 915
      guchar buf[YRANGE_WIDTH * 3];
      guchar pix[3];
Elliot Lee's avatar
Elliot Lee committed
916 917 918

      for (i = 0; i < YRANGE_HEIGHT; i++)
	{
919
	  switch (sel_channel)
920
	    {
Michael Natterer's avatar
Michael Natterer committed
921 922
	    case GIMP_HISTOGRAM_VALUE:
	    case GIMP_HISTOGRAM_ALPHA:
923 924 925
	      pix[0] = pix[1] = pix[2] = (255 - i);
	      break;

Michael Natterer's avatar
Michael Natterer committed
926 927 928
	    case GIMP_HISTOGRAM_RED:
	    case GIMP_HISTOGRAM_GREEN:
	    case GIMP_HISTOGRAM_BLUE:
929
	      pix[0] = pix[1] = pix[2] = 0;
930
	      pix[sel_channel - 1] = (255 - i);
931 932
	      break;

933
	    default:
934 935 936
	      g_warning ("unknown channel type %d, can't happen!?!?",
			 cd->channel);
	      break;
937
	    }
938 939

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

942 943
	  gtk_preview_draw_row (GTK_PREVIEW (cd->yrange),
				buf, 0, i, YRANGE_WIDTH);
Elliot Lee's avatar
Elliot Lee committed
944 945 946 947 948 949 950 951 952 953 954
	}

      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],
955 956
			  TRUE, 0, 0,
			  GRAPH_WIDTH + RADIUS * 2, GRAPH_HEIGHT + RADIUS * 2);
Elliot Lee's avatar
Elliot Lee committed
957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977

      /*  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  */
978
      if (cd->curve_type[cd->channel] == SMOOTH)
Elliot Lee's avatar
Elliot Lee committed
979 980 981 982 983 984