decompose.c 33.6 KB
Newer Older
1
/* GIMP - The GNU Image Manipulation Program
Elliot Lee's avatar
Elliot Lee committed
2
 * Copyright (C) 1995 Spencer Kimball and Peter Mattis
3
 *
Elliot Lee's avatar
Elliot Lee committed
4
 * Decompose plug-in (C) 1997 Peter Kirchgessner
5
 * e-mail: peter@kirchgessner.net, WWW: http://www.kirchgessner.net
Elliot Lee's avatar
Elliot Lee committed
6
 *
7 8 9
 * Copyright 2013 Martijn van Beers <mail_dev@martijn.at>
 * Copyright 2013 Téo Mazars        <teo.mazars@ensimag.fr>
 *
10
 * This program is free software: you can redistribute it and/or modify
Elliot Lee's avatar
Elliot Lee committed
11
 * it under the terms of the GNU General Public License as published by
12
 * the Free Software Foundation; either version 3 of the License, or
Elliot Lee's avatar
Elliot Lee committed
13 14 15 16 17 18 19 20
 * (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
21
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
Elliot Lee's avatar
Elliot Lee committed
22 23
 */

24 25
/*  Lab colorspace support originally written by Alexey Dyachenko,
 *  merged into the officical plug-in by Sven Neumann.
Elliot Lee's avatar
Elliot Lee committed
26
 */
27

28

29
#include "config.h"
Elliot Lee's avatar
Elliot Lee committed
30 31

#include <string.h>
32

33 34
#include <libgimp/gimp.h>
#include <libgimp/gimpui.h>
35

36
#include "libgimp/stdplugins-intl.h"
Elliot Lee's avatar
Elliot Lee committed
37

38 39 40
#define PLUG_IN_PROC      "plug-in-decompose"
#define PLUG_IN_PROC_REG  "plug-in-decompose-registered"
#define PLUG_IN_BINARY    "decompose"
41
#define PLUG_IN_ROLE      "gimp-decompose"
42 43


44 45 46
/* Descrition of a component */
typedef struct
{
47 48
  const gchar    *babl_name;           /* channel's  babl_component name    */
  const gchar    *channel_name;        /* name of channel to extract        */
49

50 51 52
  const gdouble   range_min;           /* min and max                       */
  const gdouble   range_max;
  const gboolean  perceptual_channel;  /* "correct" the channel in Y' space */
53

54
} Component;
55 56 57 58 59 60 61 62 63 64


/* Maximum number of images/layers generated by an extraction */
#define MAX_EXTRACT_IMAGES 4

/* Description of an extraction */
typedef struct
{
  const gchar     *type;        /* What to extract */
  const gchar     *model;       /* the babl_model string to use */
65 66
  const gboolean   dialog;      /* Set to TRUE if you want
                                 * this extract function in the dialog */
67 68 69 70 71
  const gint       num_images;  /* Number of images to create */

  const gboolean   clamp;       /* clamping values in [0.0, 1.0] */

                                /* the babl_component names of the channels */
72
  const Component  component[MAX_EXTRACT_IMAGES];
73

74
} Extract;
75 76 77 78 79 80

typedef struct
{
  gchar     extract_type[32];
  gboolean  as_layers;
  gboolean  use_registration;
81
} DecomposeVals;
82 83


Elliot Lee's avatar
Elliot Lee committed
84 85
/* Declare local functions
 */
86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115
static void      query                       (void);
static void      run                         (const gchar         *name,
                                              gint                 nparams,
                                              const GimpParam     *param,
                                              gint                *nreturn_vals,
                                              GimpParam          **return_vals);
static gint32    decompose                   (gint32               image_id,
                                              gint32               drawable_ID,
                                              const gchar         *extract_type,
                                              gint32              *image_ID_dst,
                                              gint32              *num_layers,
                                              gint32              *layer_ID_dst);
static gint32    create_new_image            (const gchar         *filename,
                                              const gchar         *layername,
                                              guint                width,
                                              guint                height,
                                              GimpImageBaseType    type,
                                              GimpPrecision        precision,
                                              gdouble              xres,
                                              gdouble              yres,
                                              gint32              *layer_ID);
static gint32    create_new_layer            (gint32               image_ID,
                                              gint                 position,
                                              const gchar         *layername,
                                              guint                width,
                                              guint                height,
                                              GimpImageBaseType    type);
static void      transfer_registration_color (GeglBuffer          *src,
                                              GeglBuffer         **dst,
                                              gint                 count);
116 117
static void      cpn_affine_transform_clamp  (GeglBuffer          *buffer,
                                              gdouble              min,
118 119
                                              gdouble              max,
                                              gboolean             clamp);
120 121
static void      copy_n_components           (GeglBuffer          *src,
                                              GeglBuffer         **dst,
122
                                              Extract              ext);
123 124 125
static void      copy_one_component          (GeglBuffer          *src,
                                              GeglBuffer          *dst,
                                              const char          *model,
126
                                              const Component      component,
127
                                              gboolean             clamp);
128 129 130 131
static gboolean  decompose_dialog            (void);
static gchar   * generate_filename           (guint32              image_ID,
                                              guint                colorspace,
                                              guint                channel);
132

Elliot Lee's avatar
Elliot Lee committed
133

134 135 136 137
#define CPN_RGBA_R      { "R",          N_("red"),           0.0, 1.0, FALSE }
#define CPN_RGBA_G      { "G",          N_("green"),         0.0, 1.0, FALSE }
#define CPN_RGBA_B      { "B",          N_("blue"),          0.0, 1.0, FALSE }
#define CPN_RGBA_A      { "A",          N_("alpha"),         0.0, 1.0, TRUE  }
Elliot Lee's avatar
Elliot Lee committed
138

139 140 141
#define CPN_HSV_H       { "hue",        N_("hue"),           0.0, 1.0, TRUE }
#define CPN_HSV_S       { "saturation", N_("saturation"),    0.0, 1.0, TRUE }
#define CPN_HSV_V       { "value",      N_("value"),         0.0, 1.0, TRUE }
142

143 144 145
#define CPN_HSL_H       { "hue",        N_("hue"),           0.0, 1.0, TRUE }
#define CPN_HSL_S       { "saturation", N_("saturation"),    0.0, 1.0, TRUE }
#define CPN_HSL_L       { "lightness",  N_("lightness"),     0.0, 1.0, TRUE }
146

147 148 149 150
#define CPN_CMYK_C      { "cyan",       N_("cyan-k"),        0.0, 1.0, TRUE }
#define CPN_CMYK_M      { "magenta",    N_("magenta-k"),     0.0, 1.0, TRUE }
#define CPN_CMYK_Y      { "yellow",     N_("yellow-k"),      0.0, 1.0, TRUE }
#define CPN_CMYK_K      { "key",        N_("black"),         0.0, 1.0, TRUE }
151

152 153 154
#define CPN_CMY_C       { "cyan",       N_("cyan"),          0.0, 1.0, TRUE }
#define CPN_CMY_M       { "magenta",    N_("magenta"),       0.0, 1.0, TRUE }
#define CPN_CMY_Y       { "yellow",     N_("yellow"),        0.0, 1.0, TRUE }
155

156 157 158
#define CPN_LAB_L       { "CIE L",      N_("L"),             0.0, 100.0, TRUE }
#define CPN_LAB_A       { "CIE a",      N_("A"),          -127.5, 127.5, TRUE }
#define CPN_LAB_B       { "CIE b",      N_("B"),          -127.5, 127.5, TRUE }
159

160 161 162
#define CPN_LCH_L       { "CIE L",      N_("L"),             0.0, 100.0, TRUE }
#define CPN_LCH_C       { "CIE C(ab)",  N_("C"),             0.0, 200.0, TRUE }
#define CPN_LCH_H       { "CIE H(ab)",  N_("H"),             0.0, 360.0, TRUE }
Elliot Lee's avatar
Elliot Lee committed
163

164 165 166
#define CPN_YCBCR_Y     { "Y'",         N_("luma-y470"),       0.0, 1.0, TRUE }
#define CPN_YCBCR_CB    { "Cb",         N_("blueness-cb470"), -0.5, 0.5, TRUE }
#define CPN_YCBCR_CR    { "Cr",         N_("redness-cr470"),  -0.5, 0.5, TRUE }
167

168 169 170
#define CPN_YCBCR709_Y  { "Y'",         N_("luma-y709"),       0.0, 1.0, TRUE }
#define CPN_YCBCR709_CB { "Cb",         N_("blueness-cb709"), -0.5, 0.5, TRUE }
#define CPN_YCBCR709_CR { "Cr",         N_("redness-cr709"),  -0.5, 0.5, TRUE }
171 172


173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212
static const Extract extract[] =
{
  { N_("RGB"),   "RGB",  TRUE,  3, FALSE, { CPN_RGBA_R, CPN_RGBA_G, CPN_RGBA_B } },
  { N_("RGBA"),  "RGBA", TRUE,  4, FALSE, { CPN_RGBA_R, CPN_RGBA_G, CPN_RGBA_B, CPN_RGBA_A } },

  { N_("Red"),   "RGB",  FALSE, 1, FALSE, { CPN_RGBA_R } },
  { N_("Green"), "RGB",  FALSE, 1, FALSE, { CPN_RGBA_G } },
  { N_("Blue"),  "RGB",  FALSE, 1, FALSE, { CPN_RGBA_B } },
  { N_("Alpha"), "RGBA", TRUE , 1, FALSE, { CPN_RGBA_A } },

  { N_("HSV"),        "HSV",  TRUE,  3, FALSE, { CPN_HSV_H, CPN_HSV_S, CPN_HSV_V } },
  { N_("Hue"),        "HSV",  FALSE, 1, FALSE, { CPN_HSV_H } },
  { N_("Saturation"), "HSV",  FALSE, 1, FALSE, { CPN_HSV_S } },
  { N_("Value"),      "HSV",  FALSE, 1, FALSE, { CPN_HSV_V } },

  { N_("HSL"),              "HSL", TRUE,  3, FALSE, { CPN_HSL_H, CPN_HSL_S, CPN_HSL_L } },
  { N_("Hue (HSL)"),        "HSL", FALSE, 1, FALSE, { CPN_HSL_H } },
  { N_("Saturation (HSL)"), "HSL", FALSE, 1, FALSE, { CPN_HSL_S } },
  { N_("Lightness"),        "HSL", FALSE, 1, FALSE, { CPN_HSL_L } },

  { N_("CMY"),     "CMY", TRUE,  3, FALSE, { CPN_CMY_C, CPN_CMY_M, CPN_CMY_Y } },
  { N_("Cyan"),    "CMY", FALSE, 1, FALSE, { CPN_CMY_C } },
  { N_("Magenta"), "CMY", FALSE, 1, FALSE, { CPN_CMY_M } },
  { N_("Yellow"),  "CMY", FALSE, 1, FALSE, { CPN_CMY_Y } },

  { N_("CMYK"),      "CMYK", TRUE,  4, FALSE, { CPN_CMYK_C, CPN_CMYK_M, CPN_CMYK_Y, CPN_CMYK_K } },
  { N_("Cyan_K"),    "CMYK", FALSE, 1, FALSE, { CPN_CMYK_C } },
  { N_("Magenta_K"), "CMYK", FALSE, 1, FALSE, { CPN_CMYK_M } },
  { N_("Yellow_K"),  "CMYK", FALSE, 1, FALSE, { CPN_CMYK_Y } },

  { N_("LAB"), "CIE Lab",     TRUE, 3, FALSE, { CPN_LAB_L, CPN_LAB_A, CPN_LAB_B } },

  { N_("LCH"), "CIE LCH(ab)", TRUE, 3, FALSE, { CPN_LCH_L, CPN_LCH_C, CPN_LCH_H } },

  { N_("YCbCr_ITU_R470"),     "Y'CbCr", TRUE, 3, FALSE, { CPN_YCBCR_Y, CPN_YCBCR_CB, CPN_YCBCR_CR} },
  { N_("YCbCr_ITU_R470_256"), "Y'CbCr", TRUE, 3, TRUE,  { CPN_YCBCR_Y, CPN_YCBCR_CB, CPN_YCBCR_CR} },

  { N_("YCbCr_ITU_R709"),     "Y'CbCr709", TRUE, 3, FALSE, { CPN_YCBCR709_Y, CPN_YCBCR709_CB, CPN_YCBCR709_CR} },
  { N_("YCbCr_ITU_R709_256"), "Y'CbCr709", TRUE, 3, TRUE,  { CPN_YCBCR709_Y, CPN_YCBCR709_CB, CPN_YCBCR709_CR} }
};
Elliot Lee's avatar
Elliot Lee committed
213

214
const GimpPlugInInfo PLUG_IN_INFO =
Elliot Lee's avatar
Elliot Lee committed
215
{
216 217 218 219
  NULL,  /* init_proc  */
  NULL,  /* quit_proc  */
  query, /* query_proc */
  run,   /* run_proc   */
Elliot Lee's avatar
Elliot Lee committed
220 221
};

222
static DecomposeVals decovals =
Elliot Lee's avatar
Elliot Lee committed
223
{
224
  "rgb",    /* Decompose type      */
225 226
  TRUE,     /* Decompose to Layers */
  FALSE     /* use registration color */
Elliot Lee's avatar
Elliot Lee committed
227 228 229 230 231
};


MAIN ()

232

Elliot Lee's avatar
Elliot Lee committed
233
static void
234
query (void)
Elliot Lee's avatar
Elliot Lee committed
235
{
236
  static GimpParamDef args[] =
Elliot Lee's avatar
Elliot Lee committed
237
  {
238
    { GIMP_PDB_INT32,    "run-mode",       "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
239 240
    { GIMP_PDB_IMAGE,    "image",          "Input image (unused)"         },
    { GIMP_PDB_DRAWABLE, "drawable",       "Input drawable"               },
241
    { GIMP_PDB_STRING,   "decompose-type", NULL                           },
242
    { GIMP_PDB_INT32,    "layers-mode",    "Create channels as layers in a single image" }
Elliot Lee's avatar
Elliot Lee committed
243
  };
244
  static const GimpParamDef return_vals[] =
Elliot Lee's avatar
Elliot Lee committed
245
  {
246 247 248 249
    { GIMP_PDB_IMAGE, "new-image", "Output gray image" },
    { GIMP_PDB_IMAGE, "new-image", "Output gray image (N/A for single channel extract)" },
    { GIMP_PDB_IMAGE, "new-image", "Output gray image (N/A for single channel extract)" },
    { GIMP_PDB_IMAGE, "new-image", "Output gray image (N/A for single channel extract)" }
Elliot Lee's avatar
Elliot Lee committed
250 251
  };

252
  GString *type_desc;
253
  gint     i;
254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269

  type_desc = g_string_new ("What to decompose: ");
  g_string_append_c (type_desc, '"');
  g_string_append (type_desc, extract[0].type);
  g_string_append_c (type_desc, '"');

  for (i = 1; i < G_N_ELEMENTS (extract); i++)
    {
      g_string_append (type_desc, ", ");
      g_string_append_c (type_desc, '"');
      g_string_append (type_desc, extract[i].type);
      g_string_append_c (type_desc, '"');
    }

  args[3].description = type_desc->str;

270
  gimp_install_procedure (PLUG_IN_PROC,
David Odin's avatar
David Odin committed
271 272 273 274 275 276 277 278 279 280
                          N_("Decompose an image into separate colorspace components"),
                          "This function creates new gray images with "
                          "different channel information in each of them",
                          "Peter Kirchgessner",
                          "Peter Kirchgessner",
                          "1997",
                          N_("_Decompose..."),
                          "RGB*",
                          GIMP_PLUGIN,
                          G_N_ELEMENTS (args),
281
                          G_N_ELEMENTS (return_vals),
David Odin's avatar
David Odin committed
282
                          args, return_vals);
283

284
  gimp_install_procedure (PLUG_IN_PROC_REG,
David Odin's avatar
David Odin committed
285 286 287 288
                          N_("Decompose an image into separate colorspace components"),
                          "This function creates new gray images with "
                          "different channel information in each of them. "
                          "Pixels in the foreground color will appear black "
289 290 291
                          "in all output images.  This can be used for "
                          "things like crop marks that have to show up on "
                          "all channels.",
David Odin's avatar
David Odin committed
292 293 294 295 296 297 298
                          "Peter Kirchgessner",
                          "Peter Kirchgessner, Clarence Risher",
                          "1997",
                          N_("_Decompose..."),
                          "RGB*",
                          GIMP_PLUGIN,
                          G_N_ELEMENTS (args),
299
                          G_N_ELEMENTS (return_vals),
David Odin's avatar
David Odin committed
300
                          args, return_vals);
301 302

  gimp_plugin_menu_register (PLUG_IN_PROC_REG, "<Image>/Colors/Components");
303 304

  g_string_free (type_desc, TRUE);
Elliot Lee's avatar
Elliot Lee committed
305 306
}

307
static void
308 309 310 311 312
run (const gchar      *name,
     gint              nparams,
     const GimpParam  *param,
     gint             *nreturn_vals,
     GimpParam       **return_vals)
Elliot Lee's avatar
Elliot Lee committed
313
{
314
  static GimpParam  values[MAX_EXTRACT_IMAGES + 1];
Sven Neumann's avatar
Sven Neumann committed
315
  GimpPDBStatusType status = GIMP_PDB_SUCCESS;
316
  GimpRunMode       run_mode;
317 318
  gint32            num_images;
  gint32            image_ID_extract[MAX_EXTRACT_IMAGES];
319
  gint32            layer_ID_extract[MAX_EXTRACT_IMAGES];
320
  gint              j;
321 322 323
  gint32            layer;
  gint32            num_layers;
  gint32            image_ID;
Elliot Lee's avatar
Elliot Lee committed
324

325
  INIT_I18N ();
326
  gegl_init (NULL, NULL);
327

Elliot Lee's avatar
Elliot Lee committed
328
  run_mode = param[0].data.d_int32;
329 330
  image_ID = param[1].data.d_image;
  layer    = param[2].data.d_drawable;
Elliot Lee's avatar
Elliot Lee committed
331

332
  *nreturn_vals = MAX_EXTRACT_IMAGES + 1;
333
  *return_vals  = values;
Elliot Lee's avatar
Elliot Lee committed
334

335
  values[0].type          = GIMP_PDB_STATUS;
Elliot Lee's avatar
Elliot Lee committed
336
  values[0].data.d_status = status;
337

Elliot Lee's avatar
Elliot Lee committed
338
  for (j = 0; j < MAX_EXTRACT_IMAGES; j++)
339
    {
340
      values[j+1].type         = GIMP_PDB_IMAGE;
341 342
      values[j+1].data.d_int32 = -1;
    }
Elliot Lee's avatar
Elliot Lee committed
343 344 345

  switch (run_mode)
    {
Sven Neumann's avatar
Sven Neumann committed
346
    case GIMP_RUN_INTERACTIVE:
Elliot Lee's avatar
Elliot Lee committed
347
      /*  Possibly retrieve data  */
348
      gimp_get_data (PLUG_IN_PROC, &decovals);
Elliot Lee's avatar
Elliot Lee committed
349 350 351

      /*  First acquire information with a dialog  */
      if (! decompose_dialog ())
David Odin's avatar
David Odin committed
352
        return;
Elliot Lee's avatar
Elliot Lee committed
353 354
      break;

Sven Neumann's avatar
Sven Neumann committed
355
    case GIMP_RUN_NONINTERACTIVE:
Elliot Lee's avatar
Elliot Lee committed
356
      /*  Make sure all the arguments are there!  */
357
      if (nparams != 4 && nparams != 5 && nparams != 6)
David Odin's avatar
David Odin committed
358 359 360
        {
          status = GIMP_PDB_CALLING_ERROR;
        }
361
      else
David Odin's avatar
David Odin committed
362
        {
Elliot Lee's avatar
Elliot Lee committed
363 364
          strncpy (decovals.extract_type, param[3].data.d_string,
                   sizeof (decovals.extract_type));
365
          decovals.extract_type[sizeof (decovals.extract_type) - 1] = '\0';
366 367

          decovals.as_layers = nparams > 4 ? param[4].data.d_int32 : FALSE;
368
          decovals.use_registration = (strcmp (name, PLUG_IN_PROC_REG) == 0);
David Odin's avatar
David Odin committed
369
        }
Elliot Lee's avatar
Elliot Lee committed
370 371
      break;

Sven Neumann's avatar
Sven Neumann committed
372
    case GIMP_RUN_WITH_LAST_VALS:
Elliot Lee's avatar
Elliot Lee committed
373
      /*  Possibly retrieve data  */
374
      gimp_get_data (PLUG_IN_PROC, &decovals);
Elliot Lee's avatar
Elliot Lee committed
375 376 377 378 379 380
      break;

    default:
      break;
    }

Sven Neumann's avatar
Sven Neumann committed
381
  if (status == GIMP_PDB_SUCCESS)
Elliot Lee's avatar
Elliot Lee committed
382
    {
383
      gimp_progress_init (_("Decomposing"));
384

385 386 387 388 389
      num_images = decompose (image_ID, layer,
                              decovals.extract_type,
                              image_ID_extract,
                              &num_layers,
                              layer_ID_extract);
390

Elliot Lee's avatar
Elliot Lee committed
391
      if (num_images <= 0)
David Odin's avatar
David Odin committed
392 393 394
        {
          status = GIMP_PDB_EXECUTION_ERROR;
        }
Elliot Lee's avatar
Elliot Lee committed
395
      else
David Odin's avatar
David Odin committed
396
        {
397 398
          /* create decompose-data parasite */
          GString *data = g_string_new ("");
399 400 401 402

          g_string_printf (data, "source=%d type=%s ",
                           layer, decovals.extract_type);

403
          for (j = 0; j < num_layers; j++)
404
            g_string_append_printf (data, "%d ", layer_ID_extract[j]);
405

David Odin's avatar
David Odin committed
406 407
          for (j = 0; j < num_images; j++)
            {
408 409
              GimpParasite *parasite;

David Odin's avatar
David Odin committed
410
              values[j+1].data.d_int32 = image_ID_extract[j];
411

David Odin's avatar
David Odin committed
412 413
              gimp_image_undo_enable (image_ID_extract[j]);
              gimp_image_clean_all (image_ID_extract[j]);
414

415 416
              parasite = gimp_parasite_new ("decompose-data",
                                            0, data->len + 1, data->str);
417
              gimp_image_attach_parasite (image_ID_extract[j], parasite);
418
              gimp_parasite_free (parasite);
419

David Odin's avatar
David Odin committed
420 421 422
              if (run_mode != GIMP_RUN_NONINTERACTIVE)
                gimp_display_new (image_ID_extract[j]);
            }
423

David Odin's avatar
David Odin committed
424 425
          /*  Store data  */
          if (run_mode == GIMP_RUN_INTERACTIVE)
426
            gimp_set_data (PLUG_IN_PROC, &decovals, sizeof (DecomposeVals));
David Odin's avatar
David Odin committed
427
        }
428 429

      gimp_progress_end ();
Elliot Lee's avatar
Elliot Lee committed
430
    }
431

Elliot Lee's avatar
Elliot Lee committed
432 433 434 435 436
  values[0].data.d_status = status;
}


/* Decompose an image. It returns the number of new (gray) images.
437 438 439
 * The image IDs for the new images are returned in image_ID_dst.
 * On failure, -1 is returned.
 */
Elliot Lee's avatar
Elliot Lee committed
440
static gint32
441 442 443 444 445 446
decompose (gint32       image_ID,
           gint32       drawable_ID,
           const gchar *extract_type,
           gint32      *image_ID_dst,
           gint32      *nlayers,
           gint32      *layer_ID_dst)
Elliot Lee's avatar
Elliot Lee committed
447
{
448 449 450 451 452 453
  const gchar   *layername;
  gint           j, extract_idx;
  gint           height, width, num_layers;
  GeglBuffer    *src_buffer;
  GeglBuffer    *dst_buffer[MAX_EXTRACT_IMAGES];
  GimpPrecision  precision;
454 455
  gboolean       requirments      = FALSE;
  gboolean       decomp_has_alpha = FALSE;
Elliot Lee's avatar
Elliot Lee committed
456 457

  extract_idx = -1;   /* Search extract type */
458
  for (j = 0; j < G_N_ELEMENTS (extract); j++)
Elliot Lee's avatar
Elliot Lee committed
459
    {
460
      if (g_ascii_strcasecmp (extract_type, extract[j].type) == 0)
David Odin's avatar
David Odin committed
461 462 463 464
        {
          extract_idx = j;
          break;
        }
Elliot Lee's avatar
Elliot Lee committed
465
    }
466 467 468
  if (extract_idx < 0)
    return -1;

469 470 471 472 473 474
  num_layers = extract[extract_idx].num_images;

  /* Sanity checks */
  src_buffer = gimp_drawable_get_buffer (drawable_ID);
  precision  = gimp_image_get_precision (image_ID);

475
  for (j = 0; j < num_layers; j++)
476
    {
477
      /* FIXME: Not 100% reliable */
478 479
      decomp_has_alpha |= ! g_strcmp0 ("alpha", extract[extract_idx].component[j].babl_name);
      decomp_has_alpha |= ! g_strcmp0 ("A",     extract[extract_idx].component[j].babl_name);
480
    }
481 482 483 484 485 486 487 488 489 490

  requirments |= (gimp_drawable_is_rgb (drawable_ID));
  requirments |= (gimp_drawable_is_indexed (drawable_ID));
  requirments |= (gimp_drawable_is_gray (drawable_ID)
                  && gimp_drawable_has_alpha (drawable_ID)
                  && (num_layers <= 2)
                  && decomp_has_alpha);
  requirments &= (!decomp_has_alpha || gimp_drawable_has_alpha (drawable_ID));

  if (!requirments)
491
    {
492
      g_message (_("Image not suitable for this decomposition"));
493
      return -1;
494
    }
495

496 497
  width  = gegl_buffer_get_width  (src_buffer);
  height = gegl_buffer_get_height (src_buffer);
Elliot Lee's avatar
Elliot Lee committed
498 499

  /* Create all new gray images */
500
  for (j = 0; j < num_layers; j++)
501
    {
502
      gchar   *filename;
503
      gdouble  xres, yres;
504

505
      filename = generate_filename (image_ID, extract_idx, j);
506 507
      gimp_image_get_resolution (image_ID, &xres, &yres);

508 509
      if (decovals.as_layers)
        {
510
          layername = gettext (extract[extract_idx].component[j].channel_name);
511 512 513

          if (j == 0)
            image_ID_dst[j] = create_new_image (filename, layername,
514
                                                width, height, GIMP_GRAY, precision,
515
                                                xres, yres,
516
                                                layer_ID_dst + j);
517
          else
518
            layer_ID_dst[j] = create_new_layer (image_ID_dst[0], j, layername,
519
                                                width, height, GIMP_GRAY);
520 521 522 523
        }
      else
        {
          image_ID_dst[j] = create_new_image (filename, NULL,
524
                                              width, height, GIMP_GRAY, precision,
525
                                              xres, yres,
526
                                              layer_ID_dst + j);
527 528
        }

529
      g_free (filename);
530

531
      dst_buffer[j] = gimp_drawable_get_buffer (layer_ID_dst[j]);
532
    }
533 534

  copy_n_components (src_buffer, dst_buffer,
535
                     extract[extract_idx]);
536 537 538 539

  if (decovals.use_registration)
    transfer_registration_color (src_buffer, dst_buffer, num_layers);

540
  gimp_progress_update (1.0);
541

542
  g_object_unref (src_buffer);
543

544
  for (j = 0; j < num_layers; j++)
545
    {
546
      g_object_unref (dst_buffer[j]);
547
    }
548

549 550 551
  *nlayers = num_layers;

  return (decovals.as_layers ? 1 : num_layers);
Elliot Lee's avatar
Elliot Lee committed
552 553 554
}


555
/* Create an image. Returns layer_ID and image_ID */
Elliot Lee's avatar
Elliot Lee committed
556
static gint32
557 558 559 560 561 562 563 564 565
create_new_image (const gchar       *filename,
                  const gchar       *layername,
                  guint              width,
                  guint              height,
                  GimpImageBaseType  type,
                  GimpPrecision      precision,
                  gdouble            xres,
                  gdouble            yres,
                  gint32            *layer_ID)
566 567
{
  gint32 image_ID;
568

569
  image_ID = gimp_image_new_with_precision (width, height, type, precision);
570

571
  gimp_image_undo_disable (image_ID);
572
  gimp_image_set_filename (image_ID, filename);
573
  gimp_image_set_resolution (image_ID, xres, yres);
574

575
  *layer_ID = create_new_layer (image_ID, 0,
576
                                layername, width, height, type);
577 578 579 580 581 582

  return image_ID;
}


static gint32
583 584 585 586 587 588
create_new_layer (gint32             image_ID,
                  gint               position,
                  const gchar       *layername,
                  guint              width,
                  guint              height,
                  GimpImageBaseType  type)
589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605
{
  gint32        layer_ID;
  GimpImageType gdtype = GIMP_RGB_IMAGE;

  switch (type)
    {
    case GIMP_RGB:
      gdtype = GIMP_RGB_IMAGE;
      break;
    case GIMP_GRAY:
      gdtype = GIMP_GRAY_IMAGE;
      break;
    case GIMP_INDEXED:
      gdtype = GIMP_INDEXED_IMAGE;
      break;
    }

606
  if (! layername)
607 608 609
    layername = _("Background");

  layer_ID = gimp_layer_new (image_ID, layername, width, height,
610 611
                             gdtype,
                             100, GIMP_LAYER_MODE_NORMAL);
612
  gimp_image_insert_layer (image_ID, layer_ID, -1, position);
613 614

  return layer_ID;
Elliot Lee's avatar
Elliot Lee committed
615 616
}

617 618 619
/* Registration Color function */

static void
620 621 622
transfer_registration_color (GeglBuffer  *src,
                             GeglBuffer **dst,
                             gint         count)
623
{
624
  GimpRGB             color, test;
625
  GeglBufferIterator *gi;
626 627 628 629 630 631
  const Babl         *src_format;
  const Babl         *dst_format;
  gint                src_bpp;
  gint                dst_bpp;
  gint                i;
  gdouble             white;
632 633

  gimp_context_get_foreground (&color);
634
  white = 1.0;
635

636 637
  src_format = gegl_buffer_get_format (src);
  src_bpp = babl_format_get_bytes_per_pixel (src_format);
638

639 640
  dst_format = gegl_buffer_get_format (dst[0]);
  dst_bpp = babl_format_get_bytes_per_pixel (dst_format);
641

642
  gi = gegl_buffer_iterator_new (src, NULL, 0, NULL,
643
                                 GEGL_ACCESS_READ, GEGL_ABYSS_NONE);
Elliot Lee's avatar
Elliot Lee committed
644

645
  for (i = 0; i < count; i++)
646
    {
647
      gegl_buffer_iterator_add (gi, dst[i], NULL, 0, NULL,
648
                                GEGL_ACCESS_READWRITE, GEGL_ABYSS_NONE);
649
    }
Elliot Lee's avatar
Elliot Lee committed
650

651
  while (gegl_buffer_iterator_next (gi))
652
    {
653 654 655
      gpointer src_data;
      gpointer dst_data[MAX_EXTRACT_IMAGES];
      gint     j, k;
Elliot Lee's avatar
Elliot Lee committed
656

657 658
      src_data = gi->data[0];
      for (j = 0; j < count; j++)
659
        dst_data[j] = gi->data[j + 1];
Elliot Lee's avatar
Elliot Lee committed
660

661
      for (k = 0; k < gi->length; k++)
662
        {
663 664
          gulong pos = k * src_bpp;

665
          gimp_rgba_set_pixel (&test, src_format, ((guchar *)src_data) + pos);
666

667 668 669 670
          if (gimp_rgb_distance (&test, &color) < 1e-6)
            {
              for (j = 0; j < count; j++)
                {
671 672
                  gpointer data = dst_data[j];

673 674 675 676 677
                  babl_process (babl_fish (babl_format ("Y double"), dst_format),
                                &white, (guchar *)data + (k * dst_bpp), 1);
                }
            }
        }
678
    }
Elliot Lee's avatar
Elliot Lee committed
679 680
}

681
static void
682 683 684 685
cpn_affine_transform_clamp (GeglBuffer *buffer,
                            gdouble     min,
                            gdouble     max,
                            gboolean    clamp)
686 687
{
  GeglBufferIterator *gi;
688 689
  gdouble             scale  = 1.0 / (max - min);
  gdouble             offset = - min;
690 691 692 693 694

  /* We want to scale values linearly, regardless of the format of the buffer */
  gegl_buffer_set_format (buffer, babl_format ("Y double"));

  gi = gegl_buffer_iterator_new (buffer, NULL, 0, NULL,
695
                                 GEGL_ACCESS_READWRITE, GEGL_ABYSS_NONE);
696 697 698 699 700 701 702 703

  while (gegl_buffer_iterator_next (gi))
    {
      guint k;
      double *data;

      data = (double*) gi->data[0];

704 705 706 707 708 709 710 711
      if (clamp)
        {
          for (k = 0; k < gi->length; k++)
            {
              data[k] = CLAMP ((data[k] + offset) * scale, 0.0, 1.0);
            }
        }
      else
712
        {
713 714 715 716
          for (k = 0; k < gi->length; k++)
            {
              data[k] = (data[k] + offset) * scale;
            }
717 718 719 720 721
        }
    }
}

static void
722 723 724
copy_n_components (GeglBuffer  *src,
                   GeglBuffer **dst,
                   Extract      ext)
725
{
726
  gint i;
727

728
  for (i = 0; i < ext.num_images; i++)
729
    {
730
      gimp_progress_update ((gdouble) i / (gdouble) ext.num_images);
731

732
      copy_one_component (src, dst[i], ext.model, ext.component[i], ext.clamp);
733 734 735 736
    }
}

static void
737 738 739
copy_one_component (GeglBuffer      *src,
                    GeglBuffer      *dst,
                    const gchar     *model,
740
                    const Component  component,
741
                    gboolean         clamp)
742
{
743 744 745
  const Babl          *component_format;
  const Babl          *dst_format;
  GeglBuffer          *temp;
746
  const GeglRectangle *extent;
747

748 749 750
  /* We are working in linear double precison*/
  component_format = babl_format_new (babl_model (model),
                                      babl_type ("double"),
751
                                      babl_component (component.babl_name),
752
                                      NULL);
753

754
  /* We need to enforce linearity here
755
   * If the output is "Y'", the ouput of temp is already ok
756 757
   * If the output is "Y" , it will enforce gamma-decoding.
   * A bit tricky and suboptimal...
758
   */
759
  if (component.perceptual_channel)
760 761 762
    dst_format = babl_format ("Y' double");
  else
    dst_format = babl_format ("Y double");
763

764 765
  extent = gegl_buffer_get_extent (src);
  temp = gegl_buffer_new (extent, dst_format);
766

767
  /* we want to copy the component as is */
768
  gegl_buffer_set_format (temp, component_format);
769
  gegl_buffer_copy (src, NULL, GEGL_ABYSS_NONE, temp, NULL);
770

771 772 773 774 775 776 777 778
  if (component.range_min != 0.0 ||
      component.range_max != 1.0 ||
      clamp)
    {
      cpn_affine_transform_clamp (temp,
                                  component.range_min, component.range_max,
                                  clamp);
    }
779 780

  /* This is our new "Y(') double" component buffer */
781
  gegl_buffer_set_format (temp, NULL);
782

783
  /* Now we let babl convert it back to the format that dst needs */
784
  gegl_buffer_copy (temp, NULL, GEGL_ABYSS_NONE, dst, NULL);
785

786
  g_object_unref (temp);
787 788
}

789
static gboolean
Elliot Lee's avatar
Elliot Lee committed
790 791
decompose_dialog (void)
{
792
  GtkWidget *dialog;
793
  GtkWidget *main_vbox;
Elliot Lee's avatar
Elliot Lee committed
794 795
  GtkWidget *frame;
  GtkWidget *vbox;
796 797 798 799
  GtkWidget *hbox;
  GtkWidget *label;
  GtkWidget *combo;
  GtkWidget *toggle;
800
  gint       j;
801
  gint       extract_idx;
802
  gboolean   run;
Elliot Lee's avatar
Elliot Lee committed
803

804 805 806 807 808 809 810 811 812 813 814
  extract_idx = 0;
  for (j = 0; j < G_N_ELEMENTS (extract); j++)
    {
      if (extract[j].dialog &&
          g_ascii_strcasecmp (decovals.extract_type, extract[j].type) == 0)
        {
          extract_idx = j;
          break;
        }
    }

815
  gimp_ui_init (PLUG_IN_BINARY, FALSE);
Elliot Lee's avatar
Elliot Lee committed
816

817
  dialog = gimp_dialog_new (_("Decompose"), PLUG_IN_ROLE,
818 819
                            NULL, 0,
                            gimp_standard_help_func, PLUG_IN_PROC,
820

821 822
                            _("_Cancel"), GTK_RESPONSE_CANCEL,
                            _("_OK"),     GTK_RESPONSE_OK,
823

824
                            NULL);
825

826
  gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
827 828 829
                                           GTK_RESPONSE_OK,
                                           GTK_RESPONSE_CANCEL,
                                           -1);
830

831 832
  gtk_window_set_resizable (GTK_WINDOW (dialog), FALSE);
  gimp_window_set_transient (GTK_WINDOW (dialog));
833

Michael Natterer's avatar
Michael Natterer committed
834
  main_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
835
  gtk_container_set_border_width (GTK_CONTAINER (main_vbox), 12);
836 837
  gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))),
                      main_vbox, TRUE, TRUE, 0);
838 839 840 841
  gtk_widget_show (main_vbox);

  frame = gimp_frame_new (_("Extract Channels"));
  gtk_box_pack_start (GTK_BOX (main_vbox), frame, TRUE, TRUE, 0);
842
  gtk_widget_show (frame);
Elliot Lee's avatar
Elliot Lee committed
843

Michael Natterer's avatar
Michael Natterer committed
844
  vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
Elliot Lee's avatar
Elliot Lee committed
845
  gtk_container_add (GTK_CONTAINER (frame), vbox);
846
  gtk_widget_show (vbox);
Elliot Lee's avatar
Elliot Lee committed
847

Michael Natterer's avatar
Michael Natterer committed
848
  hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
849 850 851 852
  gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
  gtk_widget_show (hbox);

  label = gtk_label_new_with_mnemonic (_("Color _model:"));
853
  gtk_label_set_xalign (GTK_LABEL (label), 0.0);
854 855 856
  gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
  gtk_widget_show (label);

857
  combo = g_object_new (GIMP_TYPE_INT_COMBO_BOX, NULL);
858
  for (j = 0; j < G_N_ELEMENTS (extract); j++)
859
    {
860 861 862 863 864 865 866 867 868 869 870 871 872 873 874
      if (extract[j].dialog)
        {
          gchar *label = g_strdup (gettext (extract[j].type));
          gchar *l;

          for (l = label; *l; l++)
            if (*l == '-' || *l == '_')
              *l = ' ';

          gimp_int_combo_box_append (GIMP_INT_COMBO_BOX (combo),
                                     GIMP_INT_STORE_LABEL, label,
                                     GIMP_INT_STORE_VALUE, j,
                                     -1);
          g_free (label);
        }
875
    }
876

877 878 879 880 881 882 883 884 885
  gtk_box_pack_start (GTK_BOX (hbox), combo, TRUE, TRUE, 0);
  gtk_widget_show (combo);

  gtk_label_set_mnemonic_widget (GTK_LABEL (label), combo);

  gimp_int_combo_box_connect (GIMP_INT_COMBO_BOX (combo),
                              extract_idx,
                              G_CALLBACK (gimp_int_combo_box_get_active),
                              &extract_idx);
886

887
  toggle = gtk_check_button_new_with_mnemonic (_("_Decompose to layers"));
888 889
  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle),
                                decovals.as_layers);
890
  gtk_box_pack_start (GTK_BOX (main_vbox), toggle, FALSE, FALSE, 0);
891 892
  gtk_widget_show (toggle);

893 894 895 896
  g_signal_connect (toggle, "toggled",
                    G_CALLBACK (gimp_toggle_button_update),
                    &decovals.as_layers);

897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912
  toggle =
    gtk_check_button_new_with_mnemonic (_("_Foreground as registration color"));
  gimp_help_set_help_data (toggle, _("Pixels in the foreground color will "
                                     "appear black in all output images.  "
                                     "This can be used for things like crop "
                                     "marks that have to show up on all "
                                     "channels."), PLUG_IN_PROC);
  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle),
                                decovals.use_registration);
  gtk_box_pack_start (GTK_BOX (main_vbox), toggle, FALSE, FALSE, 0);
  gtk_widget_show (toggle);

  g_signal_connect (toggle, "toggled",
                    G_CALLBACK (gimp_toggle_button_update),
                    &decovals.use_registration);

913
  gtk_widget_show (dialog);
Elliot Lee's avatar
Elliot Lee committed
914

915
  run = (gimp_dialog_run (GIMP_DIALOG (dialog)) == GTK_RESPONSE_OK);
Elliot Lee's avatar
Elliot Lee committed
916

917
  gtk_widget_destroy (dialog);
Elliot Lee's avatar
Elliot Lee committed
918

919
  if (run)
920 921
    strncpy (decovals.extract_type, extract[extract_idx].type,
             sizeof decovals.extract_type - 1);
922 923

  return run;
Elliot Lee's avatar
Elliot Lee committed
924
}
925 926 927

/* Build a filename like <imagename>-<channel>.<extension> */
gchar *
928 929 930
generate_filename (guint32 image_ID,
                   guint   colorspace,
                   guint   channel)
931 932 933 934 935 936 937 938 939 940 941 942 943 944
{
  /* Build a filename like <imagename>-<channel>.<extension> */
  gchar   *fname;
  gchar   *filename;
  gchar   *extension;

  fname = gimp_image_get_filename (image_ID);

  if (fname)
    {
      extension = fname + strlen (fname) - 1;

      while (extension >= fname)
        {
945 946
          if (*extension == '.')
            break;
947 948
          extension--;
        }
949

950 951 952 953 954 955 956 957 958 959
      if (extension >= fname)
        {
          *(extension++) = '\0';

          if (decovals.as_layers)
            filename = g_strdup_printf ("%s-%s.%s", fname,
                                        gettext (extract[colorspace].type),
                                        extension);
          else
            filename = g_strdup_printf ("%s-%s.%s", fname,
960
                                        gettext (extract[colorspace].component[channel].channel_name),
961 962 963 964 965 966 967 968 969
                                        extension);
        }
      else
        {
          if (decovals.as_layers)
            filename = g_strdup_printf ("%s-%s", fname,
                                        gettext (extract[colorspace].type));
          else
            filename = g_strdup_printf ("%s-%s", fname,
970
                                        gettext (extract[colorspace].component[channel].channel_name));
971 972 973 974
        }
    }
  else
    {
975
      filename = g_strdup (gettext (extract[colorspace].component[channel].channel_name));
976
    }
977

978
  g_free (fname);
979

980 981
  return filename;
}