channel_mixer.c 39.2 KB
Newer Older
1 2 3 4 5
/****************************************************************************
 * This is a plugin for the GIMP v 2.0 or later.
 *
 * Copyright (C) 2002 Martin Guldahl <mguldahl@xmission.com>
 * Based on GTK code from:
6
 *    homomorphic (Copyright (C) 2001 Valter Marcus Hilden)
7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
 *    rand-noted  (Copyright (C) 1998 Miles O'Neal)
 *    nlfilt      (Copyright (C) 1997 Eric L. Hernes)
 *    pagecurl    (Copyright (C) 1996 Federico Mena Quintero)
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 ****************************************************************************/

#include "config.h"

29
#include <errno.h>
30
#include <string.h>
31

32
#include <glib/gstdio.h>
33 34 35 36 37 38 39 40 41

#include <libgimp/gimp.h>
#include <libgimp/gimpui.h>

#include "libgimp/stdplugins-intl.h"


#define PLUG_IN_NAME        "plug_in_colors_channel_mixer"
#define PLUG_IN_VERSION     "Channel Mixer 0.8"
42
#define HELP_ID             "plug-in-colors-channel-mixer"
43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70
#define PROGRESS_UPDATE_NUM 20
#define CM_LINE_SIZE        1024

typedef enum
{
  CM_RED_CHANNEL,
  CM_GREEN_CHANNEL,
  CM_BLUE_CHANNEL
} CmModeType;

typedef struct
{
  gdouble red_gain;
  gdouble green_gain;
  gdouble blue_gain;
} CmChannelType;

typedef struct
{
  CmChannelType  red;
  CmChannelType  green;
  CmChannelType  blue;
  CmChannelType  black;

  gboolean       monochrome_flag;
  gboolean       preserve_luminosity_flag;

  CmModeType     output_channel;
71
  gboolean       preview;
72 73 74 75 76

  GtkAdjustment *red_data;
  GtkAdjustment *green_data;
  GtkAdjustment *blue_data;

77
  GtkWidget     *combo;
78 79 80 81 82 83 84 85

  CmModeType     old_output_channel;

  GtkWidget     *monochrome_toggle;
  GtkWidget     *preserve_luminosity_toggle;
} CmParamsType;


86 87 88 89 90 91
static void     query (void);
static void     run   (const gchar      *name,
                       gint              nparams,
                       const GimpParam  *param,
                       gint             *nreturn_vals,
                       GimpParam       **return_vals);
92

93 94
static void cm_set_defaults (CmParamsType *mix);

95
static void     channel_mixer (GimpDrawable *drawable);
96
static gboolean cm_dialog     (GimpDrawable *drawable);
97 98 99 100 101 102 103 104 105 106 107 108 109

static void cm_red_scale_callback           (GtkAdjustment    *adjustment,
                                             CmParamsType     *mix);
static void cm_green_scale_callback         (GtkAdjustment    *adjustment,
                                             CmParamsType     *mix);
static void cm_blue_scale_callback          (GtkAdjustment    *adjustment,
                                             CmParamsType     *mix);
static void cm_monochrome_callback          (GtkWidget        *widget,
                                             CmParamsType     *mix);
static void cm_preserve_luminosity_callback (GtkWidget        *widget,
                                             CmParamsType     *mix);
static void cm_load_file_callback           (GtkWidget        *widget,
                                             CmParamsType     *mix);
110
static void cm_load_file_response_callback  (GtkWidget        *dialog,
111 112 113 114
                                             gint              response_id,
                                             CmParamsType     *mix);
static void cm_save_file_callback           (GtkWidget        *widget,
                                             CmParamsType     *mix);
115
static void cm_save_file_response_callback  (GtkWidget        *dialog,
116 117
                                             gint              response_id,
                                             CmParamsType     *mix);
118 119
static void cm_reset_callback               (GtkWidget        *widget,
                                             CmParamsType     *mix);
120
static void cm_combo_callback               (GtkWidget        *widget,
121 122 123 124 125 126 127 128 129 130 131 132 133 134
                                             CmParamsType     *mix);

static gboolean cm_force_overwrite (const gchar *filename,
                                    GtkWidget   *parent);

static gdouble cm_calculate_norm (CmParamsType  *mix,
                                  CmChannelType *ch);

static inline guchar cm_mix_pixel (CmChannelType *ch,
                                   guchar         r,
                                   guchar         g,
                                   guchar         b,
                                   gdouble        norm);

135 136
static void cm_preview       (CmParamsType *mix,
                              GimpPreview  *preview);
137
static void cm_set_adjusters (CmParamsType *mix);
138
static void cm_update_ui     (CmParamsType *mix);
139 140 141 142

static void cm_save_file (CmParamsType *mix,
                          FILE         *fp);

143

144 145 146 147 148 149 150 151
GimpPlugInInfo PLUG_IN_INFO =
{
  NULL,  /* init_proc  */
  NULL,  /* quit_proc  */
  query, /* query_proc */
  run    /* run_proc   */
};

152 153 154
static CmParamsType  mix;
static GtkWidget    *preview;

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 180 181 182 183

MAIN ()

static void
query (void)
{
  static GimpParamDef args[] =
  {
    { GIMP_PDB_INT32, "run_mode", "Interactive, non-interactive" },
    { GIMP_PDB_IMAGE, "image", "Input image (unused)" },
    { GIMP_PDB_DRAWABLE, "drawable", "Input drawable" },
    { GIMP_PDB_INT32, "monochrome", "Monochrome (TRUE or FALSE)" },
    { GIMP_PDB_FLOAT, "rr_gain", "Set the red gain for the red channel" },
    { GIMP_PDB_FLOAT, "rg_gain", "Set the green gain for the red channel" },
    { GIMP_PDB_FLOAT, "rb_gain", "Set the blue gain for the red channel" },
    { GIMP_PDB_FLOAT, "gr_gain", "Set the red gain for the green channel" },
    { GIMP_PDB_FLOAT, "gg_gain", "Set the green gain for the green channel" },
    { GIMP_PDB_FLOAT, "gb_gain", "Set the blue gain for the green channel" },
    { GIMP_PDB_FLOAT, "br_gain", "Set the red gain for the blue channel" },
    { GIMP_PDB_FLOAT, "bg_gain", "Set the green gain for the blue channel" },
    { GIMP_PDB_FLOAT, "bb_gain", "Set the blue gain for the blue channel" }
  };

  gimp_install_procedure (PLUG_IN_NAME,
                          "Mix RGB Channels.",
                          "This plug-in mixes the RGB channels.",
                          "Martin Guldahl <mguldahl@xmission.com>",
                          "Martin Guldahl <mguldahl@xmission.com>",
                          "2002",
184
                          N_("Channel Mi_xer..."),
185 186 187 188
                          "RGB*",
                          GIMP_PLUGIN,
                          G_N_ELEMENTS (args), 0,
                          args, NULL);
189

190
  gimp_plugin_menu_register (PLUG_IN_NAME, "<Image>/Filters/Colors");
191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224
}

/*----------------------------------------------------------------------
 *  run() - main routine
 *
 *  This handles the main interaction with the GIMP itself,
 *  and invokes the routine that actually does the work.
 *--------------------------------------------------------------------*/
static void
run (const gchar      *name,
     gint              nparams,
     const GimpParam  *param,
     gint             *nreturn_vals,
     GimpParam       **return_vals)
{
  static GimpParam   values[1];
  GimpDrawable      *drawable;
  GimpRunMode        run_mode;
  GimpPDBStatusType  status = GIMP_PDB_SUCCESS;

  run_mode = param[0].data.d_int32;

  INIT_I18N ();

  *nreturn_vals = 1;
  *return_vals  = values;

  values[0].type          = GIMP_PDB_STATUS;
  values[0].data.d_status = status;

  drawable = gimp_drawable_get (param[2].data.d_drawable);

  if (gimp_drawable_is_rgb (drawable->drawable_id))
    {
225 226 227
      cm_set_defaults (&mix);
      mix.preview = TRUE;

228 229 230 231 232
      switch (run_mode)
        {
        case GIMP_RUN_INTERACTIVE:
          gimp_get_data (PLUG_IN_NAME, &mix);

233
          if (! cm_dialog (drawable))
234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298
            {
              gimp_drawable_detach (drawable);
              return;
            }

          break;

        case GIMP_RUN_NONINTERACTIVE:
          mix.monochrome_flag = param[3].data.d_int32;

          if (mix.monochrome_flag == 1)
            {
              mix.black.red_gain   = param[4].data.d_float;
              mix.black.green_gain = param[5].data.d_float;
              mix.black.blue_gain  = param[6].data.d_float;
            }
          else
            {
              mix.red.red_gain     = param[4].data.d_float;
              mix.red.green_gain   = param[5].data.d_float;
              mix.red.blue_gain    = param[6].data.d_float;
              mix.green.red_gain   = param[7].data.d_float;
              mix.green.green_gain = param[8].data.d_float;
              mix.green.blue_gain  = param[9].data.d_float;
              mix.blue.red_gain    = param[10].data.d_float;
              mix.blue.green_gain  = param[11].data.d_float;
              mix.blue.blue_gain   = param[12].data.d_float;
            }
          break;

        case GIMP_RUN_WITH_LAST_VALS:
          gimp_get_data (PLUG_IN_NAME, &mix);
          break;

        default:
          break;
        }

      if (status == GIMP_PDB_SUCCESS)
        {
          /* printf("Channel Mixer:: Mode:%d  r %f  g %f  b %f\n ",
                 param[3].data.d_int32, mix.black.red_gain,
                 mix.black.green_gain, mix.black.blue_gain); */

          gimp_progress_init (_(PLUG_IN_VERSION));

          channel_mixer (drawable);

          if (run_mode != GIMP_RUN_NONINTERACTIVE)
            gimp_displays_flush ();

          if (run_mode == GIMP_RUN_INTERACTIVE)
            gimp_set_data (PLUG_IN_NAME, &mix, sizeof (CmParamsType));
        }
    }
  else
    {
      status = GIMP_PDB_EXECUTION_ERROR;
    }

  values[0].data.d_status = status;

  gimp_drawable_detach (drawable);
}

299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314
static void
cm_set_defaults (CmParamsType *mix)
{
  static CmParamsType defaults = {
    { 1.0, 0.0, 0.0 },
    { 0.0, 1.0, 0.0 },
    { 0.0, 0.0, 1.0 },
    { 1.0, 0.0, 0.0 },
    FALSE,
    FALSE,
    CM_RED_CHANNEL
  };

  memcpy (mix, &defaults, G_STRUCT_OFFSET (CmParamsType, preview));
}

315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339
static gdouble
cm_calculate_norm (CmParamsType  *mix,
                   CmChannelType *ch)
{
  gdouble sum;

  sum = ch->red_gain + ch->green_gain + ch->blue_gain;

  if (sum == 0.0 || mix->preserve_luminosity_flag == FALSE)
    return 1.0;

  return fabs (1 / sum);
}

static inline guchar
cm_mix_pixel (CmChannelType *ch,
              guchar         r,
              guchar         g,
              guchar         b,
              gdouble        norm)
{
  gdouble c = ch->red_gain * r + ch->green_gain * g + ch->blue_gain * b;

  c *= norm;

340
  return (guchar) CLAMP0255 (c);
341 342 343 344 345 346 347 348 349 350 351 352
}

static void
channel_mixer (GimpDrawable *drawable)
{
  GimpPixelRgn  src_rgn, dest_rgn;
  guchar       *src, *dest;
  gpointer      pr;
  gint          x, y;
  gint          total, processed = 0;
  gboolean      has_alpha;
  gdouble       red_norm, green_norm, blue_norm, black_norm;
353 354 355 356 357 358 359 360
  gint          x1, y1, x2, y2;
  gint          width, height;

  gimp_drawable_mask_bounds (drawable->drawable_id,
                             &x1, &y1, &x2, &y2);

  width  = x2 - x1;
  height = y2 - y1;
361 362 363

  has_alpha = gimp_drawable_has_alpha (drawable->drawable_id);

364
  total = width * height;
365 366 367 368

  gimp_tile_cache_ntiles (2 * (drawable->width / gimp_tile_width () + 1));

  gimp_pixel_rgn_init (&src_rgn, drawable,
369
                       x1, y1, width, height, FALSE, FALSE);
370
  gimp_pixel_rgn_init (&dest_rgn, drawable,
371
                       x1, y1, width, height, TRUE, TRUE);
372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393

  red_norm   = cm_calculate_norm (&mix, &mix.red);
  green_norm = cm_calculate_norm (&mix, &mix.green);
  blue_norm  = cm_calculate_norm (&mix, &mix.blue);
  black_norm = cm_calculate_norm (&mix, &mix.black);

  for (pr = gimp_pixel_rgns_register (2, &src_rgn, &dest_rgn);
       pr != NULL; pr = gimp_pixel_rgns_process (pr))
    {
      gint offset;

      for (y = 0; y < src_rgn.h; y++)
        {
          src  = src_rgn.data  + y * src_rgn.rowstride;
          dest = dest_rgn.data + y * dest_rgn.rowstride;

          offset = 0;

          for (x = 0; x < src_rgn.w; x++)
            {
              guchar r, g, b;

394 395 396
              r = src[offset + 0];
              g = src[offset + 1];
              b = src[offset + 2];
397 398 399

              if (mix.monochrome_flag == TRUE)
                {
400 401 402
                  dest[offset + 0] =
                  dest[offset + 1] =
                  dest[offset + 2] =
403
                    cm_mix_pixel (&mix.black, r, g, b, black_norm);
404 405 406
                }
              else
                {
407
                  dest[offset + 0] =
408
                    cm_mix_pixel (&mix.red, r, g, b, red_norm);
409
                  dest[offset + 1] =
410
                    cm_mix_pixel (&mix.green, r, g, b, green_norm);
411
                  dest[offset + 2] =
412 413 414 415 416 417 418
                    cm_mix_pixel (&mix.blue, r, g, b, blue_norm);
                }

              offset += 3;

              if (has_alpha)
                {
419
                  dest[offset] = src[offset];
420 421 422 423 424 425 426 427 428 429 430 431 432 433 434
                  offset++;
                }

              /* update progress */
              if ((++processed % (total / PROGRESS_UPDATE_NUM + 1)) == 0)
                gimp_progress_update ((gdouble) processed / (gdouble) total);
            }
        }
    }

  gimp_progress_update (1.0);

  gimp_drawable_flush (drawable);
  gimp_drawable_merge_shadow (drawable->drawable_id, TRUE);
  gimp_drawable_update (drawable->drawable_id,
435
                        x1, y1, width, height);
436 437
}

438
static gboolean
439
cm_dialog (GimpDrawable *drawable)
440 441
{
  GtkWidget *dialog;
442
  GtkWidget *main_vbox;
443 444 445 446
  GtkWidget *frame;
  GtkWidget *hbox;
  GtkWidget *button;
  GtkWidget *label;
Sven Neumann's avatar
Sven Neumann committed
447
  GtkWidget *image;
448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485
  GtkWidget *table;
  gdouble    red_value, green_value, blue_value;
  gboolean   run;

  gimp_ui_init ("channel_mixer", FALSE);

  /* get values */
  if (mix.monochrome_flag == TRUE)
    {
      red_value   = mix.black.red_gain   * 100;
      green_value = mix.black.green_gain * 100;
      blue_value  = mix.black.blue_gain  * 100;
    }
  else
    {
      switch (mix.output_channel)
        {
        case CM_RED_CHANNEL:
          red_value   = mix.red.red_gain   * 100;
          green_value = mix.red.green_gain * 100;
          blue_value  = mix.red.blue_gain  * 100;
          break;

        case CM_GREEN_CHANNEL:
          red_value   = mix.green.red_gain   * 100;
          green_value = mix.green.green_gain * 100;
          blue_value  = mix.green.blue_gain  * 100;
          break;

        case CM_BLUE_CHANNEL:
          red_value   = mix.blue.red_gain   * 100;
          green_value = mix.blue.green_gain * 100;
          blue_value  = mix.blue.blue_gain  * 100;
          break;

        default:
          g_assert_not_reached ();

486
          red_value = green_value = blue_value = 0.0;
487 488 489 490 491 492
          break;
        }
    }

  dialog = gimp_dialog_new (_("Channel Mixer"), "mixer",
                            NULL, 0,
493
                            gimp_standard_help_func, HELP_ID,
494 495 496 497

                            GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
                            GTK_STOCK_OK,     GTK_RESPONSE_OK,

498
                            NULL);
499

500 501 502 503 504
  gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
                                              GTK_RESPONSE_OK,
                                              GTK_RESPONSE_CANCEL,
                                              -1);

505 506 507 508
  main_vbox = gtk_vbox_new (FALSE, 12);
  gtk_container_set_border_width (GTK_CONTAINER (main_vbox), 12);
  gtk_container_add (GTK_CONTAINER (GTK_DIALOG (dialog)->vbox), main_vbox);
  gtk_widget_show (main_vbox);
509

510 511 512 513 514 515
  preview = gimp_aspect_preview_new (drawable, &mix.preview);
  gtk_box_pack_start_defaults (GTK_BOX (main_vbox), preview);
  gtk_widget_show (preview);
  g_signal_connect_swapped (preview, "invalidated",
                            G_CALLBACK (cm_preview),
                            &mix);
516

517
  frame = gimp_frame_new (NULL);
518
  gtk_box_pack_start (GTK_BOX (main_vbox), frame, FALSE, FALSE, 0);
519 520
  gtk_widget_show (frame);

521
  hbox = gtk_hbox_new (FALSE, 6);
522 523
  gtk_frame_set_label_widget (GTK_FRAME (frame), hbox);
  gtk_widget_show (hbox);
524

525
  label = gtk_label_new_with_mnemonic (_("O_utput channel:"));
Sven Neumann's avatar
Sven Neumann committed
526

527 528 529
  gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
  gtk_widget_show (label);

Sven Neumann's avatar
Sven Neumann committed
530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547
  mix.combo = gimp_int_combo_box_new (NULL, 0);

  gimp_int_combo_box_append (GIMP_INT_COMBO_BOX (mix.combo),
                             GIMP_INT_STORE_VALUE,    CM_RED_CHANNEL,
                             GIMP_INT_STORE_LABEL,    _("Red"),
                             GIMP_INT_STORE_STOCK_ID, GIMP_STOCK_CHANNEL_RED,
                             -1);
  gimp_int_combo_box_append (GIMP_INT_COMBO_BOX (mix.combo),
                             GIMP_INT_STORE_VALUE,    CM_GREEN_CHANNEL,
                             GIMP_INT_STORE_LABEL,    _("Green"),
                             GIMP_INT_STORE_STOCK_ID, GIMP_STOCK_CHANNEL_GREEN,
                             -1);
  gimp_int_combo_box_append (GIMP_INT_COMBO_BOX (mix.combo),
                             GIMP_INT_STORE_VALUE,    CM_BLUE_CHANNEL,
                             GIMP_INT_STORE_LABEL,    _("Blue"),
                             GIMP_INT_STORE_STOCK_ID, GIMP_STOCK_CHANNEL_BLUE,
                             -1);

548 549
  gimp_int_combo_box_set_active (GIMP_INT_COMBO_BOX (mix.combo),
                                 mix.output_channel);
550

551 552 553
  g_signal_connect (mix.combo, "changed",
                    G_CALLBACK (cm_combo_callback),
                    &mix);
554

Sven Neumann's avatar
Sven Neumann committed
555
  gtk_box_pack_start (GTK_BOX (hbox), mix.combo, TRUE, TRUE, 0);
556
  gtk_widget_show (mix.combo);
557

558 559
  if (mix.monochrome_flag)
    gtk_widget_set_sensitive (mix.combo, FALSE);
560

561
  gtk_label_set_mnemonic_widget (GTK_LABEL (label), mix.combo);
562 563 564

  /*........................................................... */

Sven Neumann's avatar
Sven Neumann committed
565
  table = gtk_table_new (3, 4, FALSE);
566 567
  gtk_table_set_row_spacings (GTK_TABLE (table), 6);
  gtk_table_set_col_spacings (GTK_TABLE (table), 6);
568 569
  gtk_container_add (GTK_CONTAINER (frame), table);
  gtk_widget_show (table);
570

Sven Neumann's avatar
Sven Neumann committed
571 572 573 574 575 576
  image = gtk_image_new_from_stock (GIMP_STOCK_CHANNEL_RED,
                                    GTK_ICON_SIZE_BUTTON);
  gtk_table_attach (GTK_TABLE (table), image,
                    0, 1, 0, 1, GTK_FILL, GTK_FILL, 0, 0);
  gtk_widget_show (image);

577
  mix.red_data =
Sven Neumann's avatar
Sven Neumann committed
578
    GTK_ADJUSTMENT (gimp_scale_entry_new (GTK_TABLE (table), 1, 0,
579 580 581 582 583
                                          _("_Red:"), 150, -1,
                                          red_value, -200.0, 200.0,
                                          1.0, 10.0, 1,
                                          TRUE, 0.0, 0.0,
                                          NULL, NULL));
584

585
  g_signal_connect (mix.red_data, "value-changed",
586 587
                    G_CALLBACK (cm_red_scale_callback),
                    &mix);
588

Sven Neumann's avatar
Sven Neumann committed
589 590 591 592 593 594
  image = gtk_image_new_from_stock (GIMP_STOCK_CHANNEL_GREEN,
                                    GTK_ICON_SIZE_BUTTON);
  gtk_table_attach (GTK_TABLE (table), image,
                    0, 1, 1, 2, GTK_FILL, GTK_FILL, 0, 0);
  gtk_widget_show (image);

595
  mix.green_data =
Sven Neumann's avatar
Sven Neumann committed
596
    GTK_ADJUSTMENT (gimp_scale_entry_new (GTK_TABLE (table), 1, 1,
597 598 599 600 601
                                          _("_Green:"), 150, -1,
                                          green_value, -200.0, 200.0,
                                          1.0, 10.0, 1,
                                          TRUE, 0.0, 0.0,
                                          NULL, NULL));
602

603
  g_signal_connect (mix.green_data, "value-changed",
604 605
                    G_CALLBACK (cm_green_scale_callback),
                    &mix);
606 607


Sven Neumann's avatar
Sven Neumann committed
608 609 610 611 612 613
  image = gtk_image_new_from_stock (GIMP_STOCK_CHANNEL_BLUE,
                                    GTK_ICON_SIZE_BUTTON);
  gtk_table_attach (GTK_TABLE (table), image,
                    0, 1, 2, 3, GTK_FILL, GTK_FILL, 0, 0);
  gtk_widget_show (image);

614
  mix.blue_data =
Sven Neumann's avatar
Sven Neumann committed
615
    GTK_ADJUSTMENT (gimp_scale_entry_new (GTK_TABLE (table), 1, 2,
616 617 618 619 620
                                          _("_Blue:"), 150, -1,
                                          blue_value, -200.0, 200.0,
                                          1.0, 10.0, 1,
                                          TRUE, 0.0, 0.0,
                                          NULL, NULL));
621

622
  g_signal_connect (mix.blue_data, "value-changed",
623 624
                    G_CALLBACK (cm_blue_scale_callback),
                    &mix);
625 626

  /*  The monochrome toggle  */
627
  mix.monochrome_toggle = gtk_check_button_new_with_mnemonic (_("_Monochrome"));
628 629
  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (mix.monochrome_toggle),
                                mix.monochrome_flag);
630
  gtk_box_pack_start (GTK_BOX (main_vbox), mix.monochrome_toggle, FALSE, FALSE, 0);
631 632
  gtk_widget_show (mix.monochrome_toggle);

633 634 635 636
  g_signal_connect (mix.monochrome_toggle, "toggled",
                    G_CALLBACK (cm_monochrome_callback),
                    &mix);

637 638
  /*  The preserve luminosity toggle  */
  mix.preserve_luminosity_toggle =
639
    gtk_check_button_new_with_mnemonic (_("Preserve _luminosity"));
640 641 642
  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON
                                (mix.preserve_luminosity_toggle),
                                mix.preserve_luminosity_flag);
643
  gtk_box_pack_start (GTK_BOX (main_vbox), mix.preserve_luminosity_toggle,
644
                      FALSE, FALSE, 0);
645 646
  gtk_widget_show (mix.preserve_luminosity_toggle);

647 648 649
  g_signal_connect (mix.preserve_luminosity_toggle, "toggled",
                    G_CALLBACK (cm_preserve_luminosity_callback),
                    &mix);
650 651 652

  /*........................................................... */
  /*  Horizontal box for file i/o  */
653
  hbox = gtk_hbox_new (FALSE, 6);
654
  gtk_box_pack_end (GTK_BOX (main_vbox), hbox, FALSE, FALSE, 0);
655
  gtk_widget_show (hbox);
656 657 658 659 660 661

  button = gtk_button_new_from_stock (GTK_STOCK_OPEN);
  gtk_container_add (GTK_CONTAINER (hbox), button);
  gtk_widget_show (button);

  g_signal_connect (button, "clicked",
662 663 664 665
                    G_CALLBACK (cm_load_file_callback),
                    &mix);

  button = gtk_button_new_from_stock (GTK_STOCK_SAVE);
666 667 668
  gtk_container_add (GTK_CONTAINER (hbox), button);
  gtk_widget_show (button);

669 670 671
  g_signal_connect (button, "clicked",
                    G_CALLBACK (cm_save_file_callback),
                    &mix);
672

673 674 675 676 677 678 679 680
  button = gtk_button_new_from_stock (GIMP_STOCK_RESET);
  gtk_container_add (GTK_CONTAINER (hbox), button);
  gtk_widget_show (button);

  g_signal_connect (button, "clicked",
                    G_CALLBACK (cm_reset_callback),
                    &mix);

681 682
  gtk_widget_show (dialog);

Manish Singh's avatar
Manish Singh committed
683
  run = (gimp_dialog_run (GIMP_DIALOG (dialog)) == GTK_RESPONSE_OK);
684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709

  gtk_widget_destroy (dialog);

  return run;
}

static void
cm_red_scale_callback (GtkAdjustment *adjustment,
                       CmParamsType  *mix)
{
  if (mix->monochrome_flag == TRUE)
    mix->black.red_gain = adjustment->value / 100.0;
  else
    switch (mix->output_channel)
      {
      case CM_RED_CHANNEL:
        mix->red.red_gain = adjustment->value / 100.0;
        break;
      case CM_GREEN_CHANNEL:
        mix->green.red_gain = adjustment->value / 100.0;
        break;
      case CM_BLUE_CHANNEL:
        mix->blue.red_gain = adjustment->value / 100.0;
        break;
      }

710
  gimp_preview_invalidate (GIMP_PREVIEW (preview));
711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732
}

static void
cm_green_scale_callback (GtkAdjustment *adjustment,
                         CmParamsType  *mix)
{
  if (mix->monochrome_flag == TRUE)
    mix->black.green_gain = adjustment->value / 100.0;
  else
    switch (mix->output_channel)
      {
      case CM_RED_CHANNEL:
        mix->red.green_gain = adjustment->value / 100.0;
        break;
      case CM_GREEN_CHANNEL:
        mix->green.green_gain = adjustment->value / 100.0;
        break;
      case CM_BLUE_CHANNEL:
        mix->blue.green_gain = adjustment->value / 100.0;
        break;
      }

733
  gimp_preview_invalidate (GIMP_PREVIEW (preview));
734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755
}

static void
cm_blue_scale_callback (GtkAdjustment *adjustment,
                        CmParamsType  *mix)
{
  if (mix->monochrome_flag == TRUE)
    mix->black.blue_gain = adjustment->value / 100.0;
  else
    switch (mix->output_channel)
      {
      case CM_RED_CHANNEL:
        mix->red.blue_gain = adjustment->value / 100.0;
        break;
      case CM_GREEN_CHANNEL:
        mix->green.blue_gain = adjustment->value / 100.0;
        break;
      case CM_BLUE_CHANNEL:
        mix->blue.blue_gain = adjustment->value / 100.0;
        break;
      }

756
  gimp_preview_invalidate (GIMP_PREVIEW (preview));
757 758 759
}

static void
760 761
cm_preview (CmParamsType *mix,
            GimpPreview  *preview)
762
{
763 764 765 766 767 768
  guchar       *dst, *src;
  gint          x, y;
  gint          offset, rowsize;
  gdouble       red_norm, green_norm, blue_norm, black_norm;
  gint          width, height, bpp;
  GimpDrawable *drawable = GIMP_ASPECT_PREVIEW (preview)->drawable;
769 770 771 772 773 774

  red_norm   = cm_calculate_norm (mix, &mix->red);
  green_norm = cm_calculate_norm (mix, &mix->green);
  blue_norm  = cm_calculate_norm (mix, &mix->blue);
  black_norm = cm_calculate_norm (mix, &mix->black);

775 776 777 778 779 780 781
  gimp_preview_get_size (GIMP_PREVIEW (preview), &width, &height);
  bpp = gimp_drawable_bpp (drawable->drawable_id);
  src = gimp_drawable_get_thumbnail_data (drawable->drawable_id,
                                          &width, &height, &bpp);

  rowsize = width * bpp;
  dst = g_new (guchar, rowsize * height);
782

783
  offset = 0;
784
  for (y = 0; y < height; y++)
785
    {
786
      for (x = 0; x < width; x++)
787 788 789
        {
          guchar r, g, b;

790 791 792
          r = src[offset + 0];
          g = src[offset + 1];
          b = src[offset + 2];
793 794 795

          if (mix->monochrome_flag == TRUE)
            {
796 797 798
              dst[offset + 0] =
              dst[offset + 1] =
              dst[offset + 2] =
799 800 801 802
                cm_mix_pixel (&mix->black, r, g, b, black_norm);
            }
          else
            {
803
              dst[offset + 0] =
804
                cm_mix_pixel (&mix->red, r, g, b, red_norm);
805
              dst[offset + 1] =
806
                cm_mix_pixel (&mix->green, r, g, b, green_norm);
807
              dst[offset + 2] =
808 809
                cm_mix_pixel (&mix->blue, r, g, b, blue_norm);
            }
810 811
          if (bpp == 4)
            dst[offset + 3] = src[offset + 3];
812

813
          offset += bpp;
814 815
        }
    }
816
  gimp_preview_draw_buffer (GIMP_PREVIEW (preview), dst, bpp * width);
817 818 819 820 821 822 823 824 825 826 827 828

  g_free (dst);
}

static void
cm_monochrome_callback (GtkWidget    *widget,
                        CmParamsType *mix)
{
  if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget)))
    {
      mix->old_output_channel = mix->output_channel;
      mix->monochrome_flag = TRUE;
829
      gtk_widget_set_sensitive (mix->combo, FALSE);
830 831 832 833 834
    }
  else
    {
      mix->output_channel = mix->old_output_channel;
      mix->monochrome_flag = FALSE;
835
      gtk_widget_set_sensitive (mix->combo, TRUE);
836 837 838 839
    }

  cm_set_adjusters (mix);

840
  gimp_preview_invalidate (GIMP_PREVIEW (preview));
841 842 843 844 845 846 847 848 849 850 851
}

static void
cm_preserve_luminosity_callback (GtkWidget    *widget,
                                 CmParamsType *mix)
{
  if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget)))
    mix->preserve_luminosity_flag = TRUE;
  else
    mix->preserve_luminosity_flag = FALSE;

852
  gimp_preview_invalidate (GIMP_PREVIEW (preview));
853 854
}

855
static gchar *
856
cm_settings_filename (void)
857
{
858 859 860 861
  return g_build_filename (gimp_directory (),
                           "channel-mixer",
                           "settings",
                           NULL);
862 863
}

864 865 866 867
static void
cm_load_file_callback (GtkWidget    *widget,
                       CmParamsType *mix)
{
868 869 870
  static GtkWidget *dialog = NULL;

  if (! dialog)
871
    {
872
      GtkWidget *parent = gtk_widget_get_toplevel (widget);
873
      gchar     *name;
874 875 876 877 878

      dialog =
        gtk_file_chooser_dialog_new (_("Load Channel Mixer Settings"),
                                     GTK_WINDOW (parent),
                                     GTK_FILE_CHOOSER_ACTION_OPEN,
879

880 881
                                     GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
                                     GTK_STOCK_OPEN,   GTK_RESPONSE_OK,
882

883
                                     NULL);
884

885 886 887 888 889
      gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
                                               GTK_RESPONSE_OK,
                                               GTK_RESPONSE_CANCEL,
                                               -1);

890
      gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK);
891 892

      g_signal_connect (dialog, "response",
893
                        G_CALLBACK (cm_load_file_response_callback),
894
                        mix);
895
      g_signal_connect (dialog, "delete-event",
896 897 898
                        G_CALLBACK (gtk_true),
                        NULL);

899 900 901 902
      name = cm_settings_filename ();
      gtk_file_chooser_set_filename (GTK_FILE_CHOOSER (dialog), name);
      g_free (name);
    }
903

904
  gtk_window_present (GTK_WINDOW (dialog));
905 906 907
}

static void
908 909 910
cm_load_file_response_callback (GtkWidget    *dialog,
                                gint          response_id,
                                CmParamsType *mix)
911 912 913 914 915
{
  FILE *fp;

  if (response_id == GTK_RESPONSE_OK)
    {
916
      gchar *filename;
917

918
      filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
919

920
      fp = g_fopen (filename, "r");
921 922

      if (fp)
923 924
        {
          gchar buf[3][CM_LINE_SIZE];
925

926 927 928
          buf[0][0] = '\0';
          buf[1][0] = '\0';
          buf[2][0] = '\0';
929

930
          fgets (buf[0], CM_LINE_SIZE - 1, fp);
931

932 933
          fscanf (fp, "%*s %s", buf[0]);
          if (strcmp (buf[0], "RED") == 0)
934
            mix->output_channel = CM_RED_CHANNEL;
935
          else if (strcmp (buf[0], "GREEN") == 0)
936
            mix->output_channel = CM_GREEN_CHANNEL;
937
          else if (strcmp (buf[0], "BLUE") == 0)
938 939
            mix->output_channel = CM_BLUE_CHANNEL;

940
          fscanf (fp, "%*s %s", buf[0]); /* preview flag, preserved for compatibility */
941

942 943
          fscanf (fp, "%*s %s", buf[0]);
          if (strcmp (buf[0], "TRUE") == 0)
944 945 946 947
            mix->monochrome_flag = TRUE;
          else
            mix->monochrome_flag = FALSE;

948 949
          fscanf (fp, "%*s %s", buf[0]);
          if (strcmp (buf[0], "TRUE") == 0)
950 951 952 953
            mix->preserve_luminosity_flag = TRUE;
          else
            mix->preserve_luminosity_flag = FALSE;

954 955 956 957
          fscanf (fp, "%*s %s %s %s", buf[0], buf[1], buf[2]);
          mix->red.red_gain   = g_ascii_strtod (buf[0], NULL);
          mix->red.green_gain = g_ascii_strtod (buf[1], NULL);
          mix->red.blue_gain  = g_ascii_strtod (buf[2], NULL);
958

959 960 961 962
          fscanf (fp, "%*s %s %s %s", buf[0], buf[1], buf[2]);
          mix->green.red_gain   = g_ascii_strtod (buf[0], NULL);
          mix->green.green_gain = g_ascii_strtod (buf[1], NULL);
          mix->green.blue_gain  = g_ascii_strtod (buf[2], NULL);
963

964 965 966 967
          fscanf (fp, "%*s %s %s %s", buf[0], buf[1], buf[2]);
          mix->blue.red_gain   = g_ascii_strtod (buf[0], NULL);
          mix->blue.green_gain = g_ascii_strtod (buf[1], NULL);
          mix->blue.blue_gain  = g_ascii_strtod (buf[2], NULL);
968

969 970 971 972
          fscanf (fp, "%*s %s %s %s", buf[0], buf[1], buf[2]);
          mix->black.red_gain   = g_ascii_strtod (buf[0], NULL);
          mix->black.green_gain = g_ascii_strtod (buf[1], NULL);
          mix->black.blue_gain  = g_ascii_strtod (buf[2], NULL);
973 974 975

          fclose (fp);

976
          cm_update_ui (mix);
977 978
        }
      else
979 980
        {
          g_message (_("Could not open '%s' for reading: %s"),
981
                     gimp_filename_to_utf8 (filename),
982
                     g_strerror (errno));
983
        }
984 985

      g_free (filename);
986 987
    }

988
  gtk_widget_hide (dialog);
989 990 991 992 993 994
}

static void
cm_save_file_callback (GtkWidget    *widget,
                       CmParamsType *mix)
{
995 996 997
  static GtkWidget *dialog = NULL;

  if (! dialog)
998
    {
999
      GtkWidget *parent = gtk_widget_get_toplevel (widget);
1000
      gchar     *name;
1001

1002 1003 1004 1005
      dialog =
        gtk_file_chooser_dialog_new (_("Save Channel Mixer Settings"),
                                     GTK_WINDOW (parent),
                                     GTK_FILE_CHOOSER_ACTION_SAVE,
1006

1007 1008
                                     GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
                                     GTK_STOCK_SAVE,   GTK_RESPONSE_OK,
1009

1010 1011
                                     NULL);

1012 1013 1014 1015 1016
      gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
                                               GTK_RESPONSE_OK,
                                               GTK_RESPONSE_CANCEL,
                                               -1);

1017
      gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK);
1018 1019

      g_signal_connect (dialog, "response",
1020
                        G_CALLBACK (cm_save_file_response_callback),
1021
                        mix);
1022
      g_signal_connect (dialog, "delete-event",
1023 1024 1025
                        G_CALLBACK (gtk_true),
                        NULL);

1026 1027 1028 1029
      name = cm_settings_filename ();
      gtk_file_chooser_set_filename (GTK_FILE_CHOOSER (dialog), name);
      g_free (name);
    }
1030

1031
  gtk_window_present (GTK_WINDOW (dialog));
1032 1033 1034
}

static void
1035 1036 1037
cm_save_file_response_callback (GtkWidget    *dialog,
                                gint          response_id,
                                CmParamsType *mix)
1038
{
1039 1040
  gchar *filename;
  FILE  *file = NULL;
1041 1042 1043

  if (response_id != GTK_RESPONSE_OK)
    {
1044
      gtk_widget_hide (dialog);
1045 1046 1047
      return;
    }

1048
  filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
1049 1050 1051
  if (! filename)
    return;

1052 1053
  if (g_file_test (filename, G_FILE_TEST_EXISTS) &&
      ! cm_force_overwrite (filename, dialog))
1054
    {
1055 1056
      g_free (filename);
      return;
1057
    }
1058

1059
  file = g_fopen (filename, "w");
1060 1061

  if (! file)
1062 1063
    {
      g_message (_("Could not open '%s' for writing: %s"),
1064
                 gimp_filename_to_utf8 (filename), g_strerror (errno));
1065
      g_free (filename);
1066 1067 1068 1069 1070
      return;
    }

  cm_save_file (mix, file);

1071
  g_message (_("Parameters were saved to '%s'"),