1 /* GIMP - The GNU Image Manipulation Program
2  * Copyright (C) 1995 Spencer Kimball and Peter Mattis
3  *
4  * Decompose plug-in (C) 1997 Peter Kirchgessner
5  * e-mail: peter@kirchgessner.net, WWW: http://www.kirchgessner.net
6  *
7  * Copyright 2013 Martijn van Beers <mail_dev@martijn.at>
8  * Copyright 2013 Téo Mazars        <teo.mazars@ensimag.fr>
9  *
10  * This program is free software: you can redistribute it and/or modify
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
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
22  */
23 
24 /*  Lab colorspace support originally written by Alexey Dyachenko,
25  *  merged into the officical plug-in by Sven Neumann.
26  */
27 
28 #include "config.h"
29 
30 #include <string.h>
31 
32 #include <libgimp/gimp.h>
33 #include <libgimp/gimpui.h>
34 
35 #include "libgimp/stdplugins-intl.h"
36 
37 #define PLUG_IN_PROC      "plug-in-decompose"
38 #define PLUG_IN_PROC_REG  "plug-in-decompose-registered"
39 #define PLUG_IN_BINARY    "decompose"
40 #define PLUG_IN_ROLE      "gimp-decompose"
41 
42 
43 /* Description of a component */
44 typedef struct
45 {
46   const gchar    *babl_name;           /* channel's  babl_component name    */
47   const gchar    *channel_name;        /* name of channel to extract        */
48 
49   const gdouble   range_min;           /* min and max                       */
50   const gdouble   range_max;
51   const gboolean  perceptual_channel;  /* "correct" the channel in Y' space */
52 
53 } Component;
54 
55 
56 /* Maximum number of images/layers generated by an extraction */
57 #define MAX_EXTRACT_IMAGES 4
58 
59 /* Description of an extraction */
60 typedef struct
61 {
62   const gchar     *type;        /* What to extract */
63   const gchar     *model;       /* the babl_model string to use */
64   const gboolean   dialog;      /* Set to TRUE if you want
65                                  * this extract function in the dialog */
66   const gint       num_images;  /* Number of images to create */
67 
68   const gboolean   clamp;       /* clamping values in [0.0, 1.0] */
69 
70                                 /* the babl_component names of the channels */
71   const Component  component[MAX_EXTRACT_IMAGES];
72 
73 } Extract;
74 
75 typedef struct
76 {
77   gchar     extract_type[32];
78   gboolean  as_layers;
79   gboolean  use_registration;
80 } DecomposeVals;
81 
82 
83 /* Declare local functions
84  */
85 static void      query                       (void);
86 static void      run                         (const gchar         *name,
87                                               gint                 nparams,
88                                               const GimpParam     *param,
89                                               gint                *nreturn_vals,
90                                               GimpParam          **return_vals);
91 static gint32    decompose                   (gint32               image_id,
92                                               gint32               drawable_ID,
93                                               const gchar         *extract_type,
94                                               gint32              *image_ID_dst,
95                                               gint32              *num_layers,
96                                               gint32              *layer_ID_dst);
97 static gint32    create_new_image            (const gchar         *filename,
98                                               const gchar         *layername,
99                                               guint                width,
100                                               guint                height,
101                                               GimpImageBaseType    type,
102                                               GimpPrecision        precision,
103                                               gdouble              xres,
104                                               gdouble              yres,
105                                               gint32              *layer_ID);
106 static gint32    create_new_layer            (gint32               image_ID,
107                                               gint                 position,
108                                               const gchar         *layername,
109                                               guint                width,
110                                               guint                height,
111                                               GimpImageBaseType    type);
112 static void      transfer_registration_color (GeglBuffer          *src,
113                                               GeglBuffer         **dst,
114                                               gint                 count);
115 static void      cpn_affine_transform_clamp  (GeglBuffer          *buffer,
116                                               gdouble              min,
117                                               gdouble              max,
118                                               gboolean             clamp);
119 static void      copy_n_components           (GeglBuffer          *src,
120                                               GeglBuffer         **dst,
121                                               Extract              ext);
122 static void      copy_one_component          (GeglBuffer          *src,
123                                               GeglBuffer          *dst,
124                                               const char          *model,
125                                               const Component      component,
126                                               gboolean             clamp);
127 static gboolean  decompose_dialog            (void);
128 static gchar   * generate_filename           (guint32              image_ID,
129                                               guint                colorspace,
130                                               guint                channel);
131 
132 
133 #define CPN_RGBA_R      { "R",          N_("red"),           0.0, 1.0, FALSE }
134 #define CPN_RGBA_G      { "G",          N_("green"),         0.0, 1.0, FALSE }
135 #define CPN_RGBA_B      { "B",          N_("blue"),          0.0, 1.0, FALSE }
136 #define CPN_RGBA_A      { "A",          N_("alpha"),         0.0, 1.0, TRUE  }
137 
138 #define CPN_HSV_H       { "hue",        N_("hue"),           0.0, 1.0, TRUE }
139 #define CPN_HSV_S       { "saturation", N_("saturation"),    0.0, 1.0, TRUE }
140 #define CPN_HSV_V       { "value",      N_("value"),         0.0, 1.0, TRUE }
141 
142 #define CPN_HSL_H       { "hue",        N_("hue"),           0.0, 1.0, TRUE }
143 #define CPN_HSL_S       { "saturation", N_("saturation"),    0.0, 1.0, TRUE }
144 #define CPN_HSL_L       { "lightness",  N_("lightness"),     0.0, 1.0, TRUE }
145 
146 #define CPN_CMYK_C      { "Cyan",       N_("cyan"),          0.0, 1.0, TRUE }
147 #define CPN_CMYK_M      { "Magenta",    N_("magenta"),       0.0, 1.0, TRUE }
148 #define CPN_CMYK_Y      { "Yellow",     N_("yellow"),        0.0, 1.0, TRUE }
149 #define CPN_CMYK_K      { "Key",        N_("black"),         0.0, 1.0, TRUE }
150 
151 #define CPN_LAB_L       { "CIE L",      N_("L"),             0.0, 100.0, TRUE }
152 #define CPN_LAB_A       { "CIE a",      N_("A"),          -127.5, 127.5, TRUE }
153 #define CPN_LAB_B       { "CIE b",      N_("B"),          -127.5, 127.5, TRUE }
154 
155 #define CPN_LCH_L       { "CIE L",      N_("L"),             0.0, 100.0, TRUE }
156 #define CPN_LCH_C       { "CIE C(ab)",  N_("C"),             0.0, 200.0, TRUE }
157 #define CPN_LCH_H       { "CIE H(ab)",  N_("H"),             0.0, 360.0, TRUE }
158 
159 #define CPN_YCBCR_Y     { "Y'",         N_("luma-y470"),       0.0, 1.0, TRUE }
160 #define CPN_YCBCR_CB    { "Cb",         N_("blueness-cb470"), -0.5, 0.5, TRUE }
161 #define CPN_YCBCR_CR    { "Cr",         N_("redness-cr470"),  -0.5, 0.5, TRUE }
162 
163 #define CPN_YCBCR709_Y  { "Y'",         N_("luma-y709"),       0.0, 1.0, TRUE }
164 #define CPN_YCBCR709_CB { "Cb",         N_("blueness-cb709"), -0.5, 0.5, TRUE }
165 #define CPN_YCBCR709_CR { "Cr",         N_("redness-cr709"),  -0.5, 0.5, TRUE }
166 
167 
168 static const Extract extract[] =
169 {
170   { N_("RGB"),   "RGB",  TRUE,  3, FALSE, { CPN_RGBA_R, CPN_RGBA_G, CPN_RGBA_B } },
171   { N_("RGBA"),  "RGBA", TRUE,  4, FALSE, { CPN_RGBA_R, CPN_RGBA_G, CPN_RGBA_B, CPN_RGBA_A } },
172 
173   { N_("Red"),   "RGB",  FALSE, 1, FALSE, { CPN_RGBA_R } },
174   { N_("Green"), "RGB",  FALSE, 1, FALSE, { CPN_RGBA_G } },
175   { N_("Blue"),  "RGB",  FALSE, 1, FALSE, { CPN_RGBA_B } },
176   { N_("Alpha"), "RGBA", TRUE , 1, FALSE, { CPN_RGBA_A } },
177 
178   { N_("HSV"),        "HSV",  TRUE,  3, FALSE, { CPN_HSV_H, CPN_HSV_S, CPN_HSV_V } },
179   { N_("Hue"),        "HSV",  FALSE, 1, FALSE, { CPN_HSV_H } },
180   { N_("Saturation"), "HSV",  FALSE, 1, FALSE, { CPN_HSV_S } },
181   { N_("Value"),      "HSV",  FALSE, 1, FALSE, { CPN_HSV_V } },
182 
183   { N_("HSL"),              "HSL", TRUE,  3, FALSE, { CPN_HSL_H, CPN_HSL_S, CPN_HSL_L } },
184   { N_("Hue (HSL)"),        "HSL", FALSE, 1, FALSE, { CPN_HSL_H } },
185   { N_("Saturation (HSL)"), "HSL", FALSE, 1, FALSE, { CPN_HSL_S } },
186   { N_("Lightness"),        "HSL", FALSE, 1, FALSE, { CPN_HSL_L } },
187 
188   { N_("CMYK"),      "CMYK", TRUE,  4, FALSE, { CPN_CMYK_C, CPN_CMYK_M, CPN_CMYK_Y, CPN_CMYK_K } },
189   { N_("Cyan"),      "CMYK", FALSE, 1, FALSE, { CPN_CMYK_C } },
190   { N_("Magenta"),   "CMYK", FALSE, 1, FALSE, { CPN_CMYK_M } },
191   { N_("Yellow"),    "CMYK", FALSE, 1, FALSE, { CPN_CMYK_Y } },
192   { N_("Black"),     "CMYK", FALSE, 1, FALSE, { CPN_CMYK_K } },
193 
194   { N_("LAB"), "CIE Lab",     TRUE, 3, FALSE, { CPN_LAB_L, CPN_LAB_A, CPN_LAB_B } },
195 
196   { N_("LCH"), "CIE LCH(ab)", TRUE, 3, FALSE, { CPN_LCH_L, CPN_LCH_C, CPN_LCH_H } },
197 
198   { N_("YCbCr_ITU_R470"),     "Y'CbCr", TRUE, 3, FALSE, { CPN_YCBCR_Y, CPN_YCBCR_CB, CPN_YCBCR_CR} },
199   { N_("YCbCr_ITU_R470_256"), "Y'CbCr", TRUE, 3, TRUE,  { CPN_YCBCR_Y, CPN_YCBCR_CB, CPN_YCBCR_CR} },
200 
201   { N_("YCbCr_ITU_R709"),     "Y'CbCr709", TRUE, 3, FALSE, { CPN_YCBCR709_Y, CPN_YCBCR709_CB, CPN_YCBCR709_CR} },
202   { N_("YCbCr_ITU_R709_256"), "Y'CbCr709", TRUE, 3, TRUE,  { CPN_YCBCR709_Y, CPN_YCBCR709_CB, CPN_YCBCR709_CR} }
203 };
204 
205 const GimpPlugInInfo PLUG_IN_INFO =
206 {
207   NULL,  /* init_proc  */
208   NULL,  /* quit_proc  */
209   query, /* query_proc */
210   run,   /* run_proc   */
211 };
212 
213 static DecomposeVals decovals =
214 {
215   "rgb",    /* Decompose type      */
216   TRUE,     /* Decompose to Layers */
217   FALSE     /* use registration color */
218 };
219 
220 
MAIN()221 MAIN ()
222 
223 
224 static void
225 query (void)
226 {
227   static GimpParamDef args[] =
228   {
229     { GIMP_PDB_INT32,    "run-mode",       "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
230     { GIMP_PDB_IMAGE,    "image",          "Input image (unused)"         },
231     { GIMP_PDB_DRAWABLE, "drawable",       "Input drawable"               },
232     { GIMP_PDB_STRING,   "decompose-type", NULL                           },
233     { GIMP_PDB_INT32,    "layers-mode",    "Create channels as layers in a single image" }
234   };
235   static const GimpParamDef return_vals[] =
236   {
237     { GIMP_PDB_IMAGE, "new-image", "Output gray image" },
238     { GIMP_PDB_IMAGE, "new-image", "Output gray image (N/A for single channel extract)" },
239     { GIMP_PDB_IMAGE, "new-image", "Output gray image (N/A for single channel extract)" },
240     { GIMP_PDB_IMAGE, "new-image", "Output gray image (N/A for single channel extract)" }
241   };
242 
243   GString *type_desc;
244   gint     i;
245 
246   type_desc = g_string_new ("What to decompose: ");
247   g_string_append_c (type_desc, '"');
248   g_string_append (type_desc, extract[0].type);
249   g_string_append_c (type_desc, '"');
250 
251   for (i = 1; i < G_N_ELEMENTS (extract); i++)
252     {
253       g_string_append (type_desc, ", ");
254       g_string_append_c (type_desc, '"');
255       g_string_append (type_desc, extract[i].type);
256       g_string_append_c (type_desc, '"');
257     }
258 
259   args[3].description = type_desc->str;
260 
261   gimp_install_procedure (PLUG_IN_PROC,
262                           N_("Decompose an image into separate colorspace components"),
263                           "This function creates new gray images with "
264                           "different channel information in each of them",
265                           "Peter Kirchgessner",
266                           "Peter Kirchgessner",
267                           "1997",
268                           N_("_Decompose..."),
269                           "RGB*",
270                           GIMP_PLUGIN,
271                           G_N_ELEMENTS (args),
272                           G_N_ELEMENTS (return_vals),
273                           args, return_vals);
274 
275   gimp_install_procedure (PLUG_IN_PROC_REG,
276                           N_("Decompose an image into separate colorspace components"),
277                           "This function creates new gray images with "
278                           "different channel information in each of them. "
279                           "Pixels in the foreground color will appear black "
280                           "in all output images.  This can be used for "
281                           "things like crop marks that have to show up on "
282                           "all channels.",
283                           "Peter Kirchgessner",
284                           "Peter Kirchgessner, Clarence Risher",
285                           "1997",
286                           N_("_Decompose..."),
287                           "RGB*",
288                           GIMP_PLUGIN,
289                           G_N_ELEMENTS (args),
290                           G_N_ELEMENTS (return_vals),
291                           args, return_vals);
292 
293   gimp_plugin_menu_register (PLUG_IN_PROC_REG, "<Image>/Colors/Components");
294 
295   g_string_free (type_desc, TRUE);
296 }
297 
298 static void
run(const gchar * name,gint nparams,const GimpParam * param,gint * nreturn_vals,GimpParam ** return_vals)299 run (const gchar      *name,
300      gint              nparams,
301      const GimpParam  *param,
302      gint             *nreturn_vals,
303      GimpParam       **return_vals)
304 {
305   static GimpParam  values[MAX_EXTRACT_IMAGES + 1];
306   GimpPDBStatusType status = GIMP_PDB_SUCCESS;
307   GimpRunMode       run_mode;
308   gint32            num_images;
309   gint32            image_ID_extract[MAX_EXTRACT_IMAGES];
310   gint32            layer_ID_extract[MAX_EXTRACT_IMAGES];
311   gint              j;
312   gint32            layer;
313   gint32            num_layers;
314   gint32            image_ID;
315 
316   INIT_I18N ();
317   gegl_init (NULL, NULL);
318 
319   run_mode = param[0].data.d_int32;
320   image_ID = param[1].data.d_image;
321   layer    = param[2].data.d_drawable;
322 
323   *nreturn_vals = MAX_EXTRACT_IMAGES + 1;
324   *return_vals  = values;
325 
326   values[0].type          = GIMP_PDB_STATUS;
327   values[0].data.d_status = status;
328 
329   for (j = 0; j < MAX_EXTRACT_IMAGES; j++)
330     {
331       values[j+1].type         = GIMP_PDB_IMAGE;
332       values[j+1].data.d_int32 = -1;
333     }
334 
335   switch (run_mode)
336     {
337     case GIMP_RUN_INTERACTIVE:
338       /*  Possibly retrieve data  */
339       gimp_get_data (PLUG_IN_PROC, &decovals);
340 
341       /*  First acquire information with a dialog  */
342       if (! decompose_dialog ())
343         return;
344       break;
345 
346     case GIMP_RUN_NONINTERACTIVE:
347       /*  Make sure all the arguments are there!  */
348       if (nparams != 4 && nparams != 5 && nparams != 6)
349         {
350           status = GIMP_PDB_CALLING_ERROR;
351         }
352       else
353         {
354           strncpy (decovals.extract_type, param[3].data.d_string,
355                    sizeof (decovals.extract_type));
356           decovals.extract_type[sizeof (decovals.extract_type) - 1] = '\0';
357 
358           decovals.as_layers = nparams > 4 ? param[4].data.d_int32 : FALSE;
359           decovals.use_registration = (strcmp (name, PLUG_IN_PROC_REG) == 0);
360         }
361       break;
362 
363     case GIMP_RUN_WITH_LAST_VALS:
364       /*  Possibly retrieve data  */
365       gimp_get_data (PLUG_IN_PROC, &decovals);
366       break;
367 
368     default:
369       break;
370     }
371 
372   if (status == GIMP_PDB_SUCCESS)
373     {
374       gimp_progress_init (_("Decomposing"));
375 
376       num_images = decompose (image_ID, layer,
377                               decovals.extract_type,
378                               image_ID_extract,
379                               &num_layers,
380                               layer_ID_extract);
381 
382       if (num_images <= 0)
383         {
384           status = GIMP_PDB_EXECUTION_ERROR;
385         }
386       else
387         {
388           /* create decompose-data parasite */
389           GString *data = g_string_new ("");
390 
391           g_string_printf (data, "source=%d type=%s ",
392                            layer, decovals.extract_type);
393 
394           for (j = 0; j < num_layers; j++)
395             g_string_append_printf (data, "%d ", layer_ID_extract[j]);
396 
397           for (j = 0; j < num_images; j++)
398             {
399               GimpParasite *parasite;
400 
401               values[j+1].data.d_int32 = image_ID_extract[j];
402 
403               gimp_image_undo_enable (image_ID_extract[j]);
404               gimp_image_clean_all (image_ID_extract[j]);
405 
406               parasite = gimp_parasite_new ("decompose-data",
407                                             0, data->len + 1, data->str);
408               gimp_image_attach_parasite (image_ID_extract[j], parasite);
409               gimp_parasite_free (parasite);
410 
411               if (run_mode != GIMP_RUN_NONINTERACTIVE)
412                 gimp_display_new (image_ID_extract[j]);
413             }
414 
415           /*  Store data  */
416           if (run_mode == GIMP_RUN_INTERACTIVE)
417             gimp_set_data (PLUG_IN_PROC, &decovals, sizeof (DecomposeVals));
418         }
419 
420       gimp_progress_end ();
421     }
422 
423   values[0].data.d_status = status;
424 }
425 
426 
427 /* Decompose an image. It returns the number of new (gray) images.
428  * The image IDs for the new images are returned in image_ID_dst.
429  * On failure, -1 is returned.
430  */
431 static gint32
decompose(gint32 image_ID,gint32 drawable_ID,const gchar * extract_type,gint32 * image_ID_dst,gint32 * nlayers,gint32 * layer_ID_dst)432 decompose (gint32       image_ID,
433            gint32       drawable_ID,
434            const gchar *extract_type,
435            gint32      *image_ID_dst,
436            gint32      *nlayers,
437            gint32      *layer_ID_dst)
438 {
439   const gchar   *layername;
440   gint           j, extract_idx;
441   gint           height, width, num_layers;
442   GeglBuffer    *src_buffer;
443   GeglBuffer    *dst_buffer[MAX_EXTRACT_IMAGES];
444   GimpPrecision  precision;
445   gboolean       requirements      = FALSE;
446   gboolean       decomp_has_alpha = FALSE;
447 
448   extract_idx = -1;   /* Search extract type */
449   for (j = 0; j < G_N_ELEMENTS (extract); j++)
450     {
451       if (g_ascii_strcasecmp (extract_type, extract[j].type) == 0)
452         {
453           extract_idx = j;
454           break;
455         }
456     }
457   if (extract_idx < 0)
458     return -1;
459 
460   num_layers = extract[extract_idx].num_images;
461 
462   /* Sanity checks */
463   src_buffer = gimp_drawable_get_buffer (drawable_ID);
464   precision  = gimp_image_get_precision (image_ID);
465 
466   for (j = 0; j < num_layers; j++)
467     {
468       /* FIXME: Not 100% reliable */
469       decomp_has_alpha |= ! g_strcmp0 ("alpha", extract[extract_idx].component[j].babl_name);
470       decomp_has_alpha |= ! g_strcmp0 ("A",     extract[extract_idx].component[j].babl_name);
471     }
472 
473   requirements |= (gimp_drawable_is_rgb (drawable_ID));
474   requirements |= (gimp_drawable_is_indexed (drawable_ID));
475   requirements |= (gimp_drawable_is_gray (drawable_ID)
476                   && gimp_drawable_has_alpha (drawable_ID)
477                   && (num_layers <= 2)
478                   && decomp_has_alpha);
479   requirements &= (!decomp_has_alpha || gimp_drawable_has_alpha (drawable_ID));
480 
481   if (!requirements)
482     {
483       g_message (_("Image not suitable for this decomposition"));
484       return -1;
485     }
486 
487   width  = gegl_buffer_get_width  (src_buffer);
488   height = gegl_buffer_get_height (src_buffer);
489 
490   /* Create all new gray images */
491   for (j = 0; j < num_layers; j++)
492     {
493       gchar   *filename;
494       gdouble  xres, yres;
495 
496       filename = generate_filename (image_ID, extract_idx, j);
497       gimp_image_get_resolution (image_ID, &xres, &yres);
498 
499       if (decovals.as_layers)
500         {
501           layername = gettext (extract[extract_idx].component[j].channel_name);
502 
503           if (j == 0)
504             image_ID_dst[j] = create_new_image (filename, layername,
505                                                 width, height, GIMP_GRAY, precision,
506                                                 xres, yres,
507                                                 layer_ID_dst + j);
508           else
509             layer_ID_dst[j] = create_new_layer (image_ID_dst[0], j, layername,
510                                                 width, height, GIMP_GRAY);
511         }
512       else
513         {
514           image_ID_dst[j] = create_new_image (filename, NULL,
515                                               width, height, GIMP_GRAY, precision,
516                                               xres, yres,
517                                               layer_ID_dst + j);
518         }
519 
520       g_free (filename);
521 
522       dst_buffer[j] = gimp_drawable_get_buffer (layer_ID_dst[j]);
523     }
524 
525   copy_n_components (src_buffer, dst_buffer,
526                      extract[extract_idx]);
527 
528   if (decovals.use_registration)
529     transfer_registration_color (src_buffer, dst_buffer, num_layers);
530 
531   gimp_progress_update (1.0);
532 
533   g_object_unref (src_buffer);
534 
535   for (j = 0; j < num_layers; j++)
536     {
537       g_object_unref (dst_buffer[j]);
538     }
539 
540   *nlayers = num_layers;
541 
542   return (decovals.as_layers ? 1 : num_layers);
543 }
544 
545 
546 /* Create an image. Returns layer_ID and image_ID */
547 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)548 create_new_image (const gchar       *filename,
549                   const gchar       *layername,
550                   guint              width,
551                   guint              height,
552                   GimpImageBaseType  type,
553                   GimpPrecision      precision,
554                   gdouble            xres,
555                   gdouble            yres,
556                   gint32            *layer_ID)
557 {
558   gint32 image_ID;
559 
560   image_ID = gimp_image_new_with_precision (width, height, type, precision);
561 
562   gimp_image_undo_disable (image_ID);
563   gimp_image_set_filename (image_ID, filename);
564   gimp_image_set_resolution (image_ID, xres, yres);
565 
566   *layer_ID = create_new_layer (image_ID, 0,
567                                 layername, width, height, type);
568 
569   return image_ID;
570 }
571 
572 
573 static gint32
create_new_layer(gint32 image_ID,gint position,const gchar * layername,guint width,guint height,GimpImageBaseType type)574 create_new_layer (gint32             image_ID,
575                   gint               position,
576                   const gchar       *layername,
577                   guint              width,
578                   guint              height,
579                   GimpImageBaseType  type)
580 {
581   gint32        layer_ID;
582   GimpImageType gdtype = GIMP_RGB_IMAGE;
583 
584   switch (type)
585     {
586     case GIMP_RGB:
587       gdtype = GIMP_RGB_IMAGE;
588       break;
589     case GIMP_GRAY:
590       gdtype = GIMP_GRAY_IMAGE;
591       break;
592     case GIMP_INDEXED:
593       gdtype = GIMP_INDEXED_IMAGE;
594       break;
595     }
596 
597   if (! layername)
598     layername = _("Background");
599 
600   layer_ID = gimp_layer_new (image_ID, layername, width, height,
601                              gdtype,
602                              100,
603                              gimp_image_get_default_new_layer_mode (image_ID));
604   gimp_image_insert_layer (image_ID, layer_ID, -1, position);
605 
606   return layer_ID;
607 }
608 
609 /* Registration Color function */
610 
611 static void
transfer_registration_color(GeglBuffer * src,GeglBuffer ** dst,gint count)612 transfer_registration_color (GeglBuffer  *src,
613                              GeglBuffer **dst,
614                              gint         count)
615 {
616   GimpRGB             color, test;
617   GeglBufferIterator *gi;
618   const Babl         *src_format;
619   const Babl         *dst_format;
620   gint                src_bpp;
621   gint                dst_bpp;
622   gint                i;
623   gdouble             white;
624 
625   gimp_context_get_foreground (&color);
626   white = 1.0;
627 
628   src_format = gegl_buffer_get_format (src);
629   src_bpp = babl_format_get_bytes_per_pixel (src_format);
630 
631   dst_format = gegl_buffer_get_format (dst[0]);
632   dst_bpp = babl_format_get_bytes_per_pixel (dst_format);
633 
634   gi = gegl_buffer_iterator_new (src, NULL, 0, NULL,
635                                  GEGL_ACCESS_READ, GEGL_ABYSS_NONE, 10);
636 
637   for (i = 0; i < count; i++)
638     {
639       gegl_buffer_iterator_add (gi, dst[i], NULL, 0, NULL,
640                                 GEGL_ACCESS_READWRITE, GEGL_ABYSS_NONE);
641     }
642 
643   while (gegl_buffer_iterator_next (gi))
644     {
645       gpointer src_data;
646       gpointer dst_data[MAX_EXTRACT_IMAGES];
647       gint     j, k;
648 
649       src_data = gi->items[0].data;
650       for (j = 0; j < count; j++)
651         dst_data[j] = gi->items[j + 1].data;
652 
653       for (k = 0; k < gi->length; k++)
654         {
655           gulong pos = k * src_bpp;
656 
657           gimp_rgba_set_pixel (&test, src_format, ((guchar *)src_data) + pos);
658 
659           if (gimp_rgb_distance (&test, &color) < 1e-6)
660             {
661               for (j = 0; j < count; j++)
662                 {
663                   gpointer data = dst_data[j];
664 
665                   babl_process (babl_fish (babl_format ("Y double"), dst_format),
666                                 &white, (guchar *)data + (k * dst_bpp), 1);
667                 }
668             }
669         }
670     }
671 }
672 
673 static void
cpn_affine_transform_clamp(GeglBuffer * buffer,gdouble min,gdouble max,gboolean clamp)674 cpn_affine_transform_clamp (GeglBuffer *buffer,
675                             gdouble     min,
676                             gdouble     max,
677                             gboolean    clamp)
678 {
679   GeglBufferIterator *gi;
680   gdouble             scale  = 1.0 / (max - min);
681   gdouble             offset = - min;
682 
683   /* We want to scale values linearly, regardless of the format of the buffer */
684   gegl_buffer_set_format (buffer, babl_format ("Y double"));
685 
686   gi = gegl_buffer_iterator_new (buffer, NULL, 0, NULL,
687                                  GEGL_ACCESS_READWRITE, GEGL_ABYSS_NONE, 1);
688 
689   while (gegl_buffer_iterator_next (gi))
690     {
691       guint k;
692       double *data;
693 
694       data = (double*) gi->items[0].data;
695 
696       if (clamp)
697         {
698           for (k = 0; k < gi->length; k++)
699             {
700               data[k] = CLAMP ((data[k] + offset) * scale, 0.0, 1.0);
701             }
702         }
703       else
704         {
705           for (k = 0; k < gi->length; k++)
706             {
707               data[k] = (data[k] + offset) * scale;
708             }
709         }
710     }
711 }
712 
713 static void
copy_n_components(GeglBuffer * src,GeglBuffer ** dst,Extract ext)714 copy_n_components (GeglBuffer  *src,
715                    GeglBuffer **dst,
716                    Extract      ext)
717 {
718   gint i;
719 
720   for (i = 0; i < ext.num_images; i++)
721     {
722       gimp_progress_update ((gdouble) i / (gdouble) ext.num_images);
723 
724       copy_one_component (src, dst[i], ext.model, ext.component[i], ext.clamp);
725     }
726 }
727 
728 static void
copy_one_component(GeglBuffer * src,GeglBuffer * dst,const gchar * model,const Component component,gboolean clamp)729 copy_one_component (GeglBuffer      *src,
730                     GeglBuffer      *dst,
731                     const gchar     *model,
732                     const Component  component,
733                     gboolean         clamp)
734 {
735   const Babl          *component_format;
736   const Babl          *dst_format;
737   GeglBuffer          *temp;
738   const GeglRectangle *extent;
739 
740   /* We are working in linear double precision */
741   component_format = babl_format_new (babl_model (model),
742                                       babl_type ("double"),
743                                       babl_component (component.babl_name),
744                                       NULL);
745 
746   /* We need to enforce linearity here
747    * If the output is "Y'", the output of temp is already ok
748    * If the output is "Y" , it will enforce gamma-decoding.
749    * A bit tricky and suboptimal...
750    */
751   if (component.perceptual_channel)
752     dst_format = babl_format ("Y' double");
753   else
754     dst_format = babl_format ("Y double");
755 
756   extent = gegl_buffer_get_extent (src);
757   temp = gegl_buffer_new (extent, dst_format);
758 
759   /* we want to copy the component as is */
760   gegl_buffer_set_format (temp, component_format);
761   gegl_buffer_copy (src, NULL, GEGL_ABYSS_NONE, temp, NULL);
762 
763   if (component.range_min != 0.0 ||
764       component.range_max != 1.0 ||
765       clamp)
766     {
767       cpn_affine_transform_clamp (temp,
768                                   component.range_min, component.range_max,
769                                   clamp);
770     }
771 
772   /* This is our new "Y(') double" component buffer */
773   gegl_buffer_set_format (temp, NULL);
774 
775   /* Now we let babl convert it back to the format that dst needs */
776   gegl_buffer_copy (temp, NULL, GEGL_ABYSS_NONE, dst, NULL);
777 
778   g_object_unref (temp);
779 }
780 
781 static gboolean
decompose_dialog(void)782 decompose_dialog (void)
783 {
784   GtkWidget *dialog;
785   GtkWidget *main_vbox;
786   GtkWidget *frame;
787   GtkWidget *vbox;
788   GtkWidget *hbox;
789   GtkWidget *label;
790   GtkWidget *combo;
791   GtkWidget *toggle;
792   gint       j;
793   gint       extract_idx;
794   gboolean   run;
795 
796   extract_idx = 0;
797   for (j = 0; j < G_N_ELEMENTS (extract); j++)
798     {
799       if (extract[j].dialog &&
800           g_ascii_strcasecmp (decovals.extract_type, extract[j].type) == 0)
801         {
802           extract_idx = j;
803           break;
804         }
805     }
806 
807   gimp_ui_init (PLUG_IN_BINARY, FALSE);
808 
809   dialog = gimp_dialog_new (_("Decompose"), PLUG_IN_ROLE,
810                             NULL, 0,
811                             gimp_standard_help_func, PLUG_IN_PROC,
812 
813                             _("_Cancel"), GTK_RESPONSE_CANCEL,
814                             _("_OK"),     GTK_RESPONSE_OK,
815 
816                             NULL);
817 
818   gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
819                                            GTK_RESPONSE_OK,
820                                            GTK_RESPONSE_CANCEL,
821                                            -1);
822 
823   gtk_window_set_resizable (GTK_WINDOW (dialog), FALSE);
824   gimp_window_set_transient (GTK_WINDOW (dialog));
825 
826   main_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
827   gtk_container_set_border_width (GTK_CONTAINER (main_vbox), 12);
828   gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))),
829                       main_vbox, TRUE, TRUE, 0);
830   gtk_widget_show (main_vbox);
831 
832   frame = gimp_frame_new (_("Extract Channels"));
833   gtk_box_pack_start (GTK_BOX (main_vbox), frame, TRUE, TRUE, 0);
834   gtk_widget_show (frame);
835 
836   vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
837   gtk_container_add (GTK_CONTAINER (frame), vbox);
838   gtk_widget_show (vbox);
839 
840   hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
841   gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
842   gtk_widget_show (hbox);
843 
844   label = gtk_label_new_with_mnemonic (_("Color _model:"));
845   gtk_label_set_xalign (GTK_LABEL (label), 0.0);
846   gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
847   gtk_widget_show (label);
848 
849   combo = g_object_new (GIMP_TYPE_INT_COMBO_BOX, NULL);
850   for (j = 0; j < G_N_ELEMENTS (extract); j++)
851     {
852       if (extract[j].dialog)
853         {
854           gchar *label = g_strdup (gettext (extract[j].type));
855           gchar *l;
856 
857           for (l = label; *l; l++)
858             if (*l == '-' || *l == '_')
859               *l = ' ';
860 
861           gimp_int_combo_box_append (GIMP_INT_COMBO_BOX (combo),
862                                      GIMP_INT_STORE_LABEL, label,
863                                      GIMP_INT_STORE_VALUE, j,
864                                      -1);
865           g_free (label);
866         }
867     }
868 
869   gtk_box_pack_start (GTK_BOX (hbox), combo, TRUE, TRUE, 0);
870   gtk_widget_show (combo);
871 
872   gtk_label_set_mnemonic_widget (GTK_LABEL (label), combo);
873 
874   gimp_int_combo_box_connect (GIMP_INT_COMBO_BOX (combo),
875                               extract_idx,
876                               G_CALLBACK (gimp_int_combo_box_get_active),
877                               &extract_idx);
878 
879   toggle = gtk_check_button_new_with_mnemonic (_("_Decompose to layers"));
880   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle),
881                                 decovals.as_layers);
882   gtk_box_pack_start (GTK_BOX (main_vbox), toggle, FALSE, FALSE, 0);
883   gtk_widget_show (toggle);
884 
885   g_signal_connect (toggle, "toggled",
886                     G_CALLBACK (gimp_toggle_button_update),
887                     &decovals.as_layers);
888 
889   toggle =
890     gtk_check_button_new_with_mnemonic (_("_Foreground as registration color"));
891   gimp_help_set_help_data (toggle, _("Pixels in the foreground color will "
892                                      "appear black in all output images.  "
893                                      "This can be used for things like crop "
894                                      "marks that have to show up on all "
895                                      "channels."), PLUG_IN_PROC);
896   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle),
897                                 decovals.use_registration);
898   gtk_box_pack_start (GTK_BOX (main_vbox), toggle, FALSE, FALSE, 0);
899   gtk_widget_show (toggle);
900 
901   g_signal_connect (toggle, "toggled",
902                     G_CALLBACK (gimp_toggle_button_update),
903                     &decovals.use_registration);
904 
905   gtk_widget_show (dialog);
906 
907   run = (gimp_dialog_run (GIMP_DIALOG (dialog)) == GTK_RESPONSE_OK);
908 
909   gtk_widget_destroy (dialog);
910 
911   if (run)
912     strncpy (decovals.extract_type, extract[extract_idx].type,
913              sizeof decovals.extract_type - 1);
914 
915   return run;
916 }
917 
918 /* Build a filename like <imagename>-<channel>.<extension> */
919 gchar *
generate_filename(guint32 image_ID,guint colorspace,guint channel)920 generate_filename (guint32 image_ID,
921                    guint   colorspace,
922                    guint   channel)
923 {
924   /* Build a filename like <imagename>-<channel>.<extension> */
925   gchar   *fname;
926   gchar   *filename;
927   gchar   *extension;
928 
929   fname = gimp_image_get_filename (image_ID);
930 
931   if (fname)
932     {
933       extension = fname + strlen (fname) - 1;
934 
935       while (extension >= fname)
936         {
937           if (*extension == '.')
938             break;
939           extension--;
940         }
941 
942       if (extension >= fname)
943         {
944           *(extension++) = '\0';
945 
946           if (decovals.as_layers)
947             filename = g_strdup_printf ("%s-%s.%s", fname,
948                                         gettext (extract[colorspace].type),
949                                         extension);
950           else
951             filename = g_strdup_printf ("%s-%s.%s", fname,
952                                         gettext (extract[colorspace].component[channel].channel_name),
953                                         extension);
954         }
955       else
956         {
957           if (decovals.as_layers)
958             filename = g_strdup_printf ("%s-%s", fname,
959                                         gettext (extract[colorspace].type));
960           else
961             filename = g_strdup_printf ("%s-%s", fname,
962                                         gettext (extract[colorspace].component[channel].channel_name));
963         }
964     }
965   else
966     {
967       filename = g_strdup (gettext (extract[colorspace].component[channel].channel_name));
968     }
969 
970   g_free (fname);
971 
972   return filename;
973 }
974