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