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

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

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

27
#include "libgimpmath/gimpmath.h"
28
#include "libgimpwidgets/gimpwidgets.h"
29

Sven Neumann's avatar
Sven Neumann committed
30 31
#include "apptypes.h"

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

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

45
#include "libgimp/gimpenv.h"
46

47 48
#include "libgimp/gimpintl.h"

49

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

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

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

82
typedef struct _Curves Curves;
83

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

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

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

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

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

101 102
static GtkWidget *channel_items[5];

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

112
/*  curves action functions  */
Michael Natterer's avatar
Michael Natterer committed
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 179
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);
180 181


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

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

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

200
  inten = value;
201

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

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

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

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

  x -= offx;
  y -= offy;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  curves_update (curves_dialog, GRAPH | DRAW);
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

     NULL);

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  gtk_widget_show (toggle);
  gtk_widget_show (hbox);

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

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

  return cd;
}

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

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

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

Elliot Lee's avatar
Elliot Lee committed
835 836 837

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

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

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

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

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

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

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

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

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

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

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

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

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

      /*  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);