/* PIKA - Photo and Image Kooker Application * a rebranding of The GNU Image Manipulation Program (created with heckimp) * A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio * * Original copyright, applying to most contents (license remains unchanged): * Copyright (C) 1995 Spencer Kimball and Peter Mattis * * Decompose plug-in (C) 1997 Peter Kirchgessner * e-mail: peter@kirchgessner.net, WWW: http://www.kirchgessner.net * * Copyright 2013 Martijn van Beers * Copyright 2013 Téo Mazars * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ /* Lab colorspace support originally written by Alexey Dyachenko, * merged into the officical plug-in by Sven Neumann. */ #include "config.h" #include #include #include #include "libpika/stdplugins-intl.h" #define PLUG_IN_PROC "plug-in-decompose" #define PLUG_IN_BINARY "decompose" #define PLUG_IN_ROLE "pika-decompose" /* Description of a component */ typedef struct { const gchar *babl_name; /* channel's babl_component name */ const gchar *channel_name; /* name of channel to extract */ const gdouble range_min; /* min and max */ const gdouble range_max; const gboolean perceptual_channel; /* "correct" the channel in Y' space */ } Component; /* 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 */ const gboolean dialog; /* Set to TRUE if you want * this extract function in the dialog */ 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 */ const Component component[MAX_EXTRACT_IMAGES]; } Extract; typedef struct _Decompose Decompose; typedef struct _DecomposeClass DecomposeClass; struct _Decompose { PikaPlugIn parent_instance; }; struct _DecomposeClass { PikaPlugInClass parent_class; }; #define DECOMPOSE_TYPE (decompose_get_type ()) #define DECOMPOSE (obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), DECOMPOSE_TYPE, Decompose)) GType decompose_get_type (void) G_GNUC_CONST; static GList * decompose_query_procedures (PikaPlugIn *plug_in); static PikaProcedure * decompose_create_procedure (PikaPlugIn *plug_in, const gchar *name); static PikaValueArray * decompose_run (PikaProcedure *procedure, PikaRunMode run_mode, PikaImage *image, gint n_drawables, PikaDrawable **drawables, const PikaValueArray *args, gpointer run_data); static gint decompose (PikaImage *image, PikaDrawable *drawable, GObject *config, PikaImage **image_dst, gint32 *num_layers, PikaLayer **layer_dst); static PikaImage * create_new_image (GFile *file, const gchar *layername, guint width, guint height, PikaImageBaseType type, PikaPrecision precision, gdouble xres, gdouble yres, PikaLayer **layer); static PikaLayer * create_new_layer (PikaImage *image, gint position, const gchar *layername, guint width, guint height, PikaImageBaseType type); static void transfer_registration_color (GeglBuffer *src, GeglBuffer **dst, gint count); static void cpn_affine_transform_clamp (GeglBuffer *buffer, gdouble min, gdouble max, gboolean clamp); static void copy_n_components (GeglBuffer *src, GeglBuffer **dst, Extract ext); static void copy_one_component (GeglBuffer *src, GeglBuffer *dst, const char *model, const Component component, gboolean clamp); static gboolean decompose_dialog (PikaProcedure *procedure, GObject *config); static gchar * generate_filename (PikaImage *image, GObject *config, guint colorspace, guint channel); G_DEFINE_TYPE (Decompose, decompose, PIKA_TYPE_PLUG_IN) PIKA_MAIN (DECOMPOSE_TYPE) DEFINE_STD_SET_I18N #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 } #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 } #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 } #define CPN_CMYK_C { "Cyan", N_("cyan"), 0.0, 1.0, TRUE } #define CPN_CMYK_M { "Magenta", N_("magenta"), 0.0, 1.0, TRUE } #define CPN_CMYK_Y { "Yellow", N_("yellow"), 0.0, 1.0, TRUE } #define CPN_CMYK_K { "Key", N_("black"), 0.0, 1.0, TRUE } #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 } #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 } #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 } #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 } 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_("CMYK"), "CMYK", TRUE, 4, FALSE, { CPN_CMYK_C, CPN_CMYK_M, CPN_CMYK_Y, CPN_CMYK_K } }, { N_("Cyan"), "CMYK", FALSE, 1, FALSE, { CPN_CMYK_C } }, { N_("Magenta"), "CMYK", FALSE, 1, FALSE, { CPN_CMYK_M } }, { N_("Yellow"), "CMYK", FALSE, 1, FALSE, { CPN_CMYK_Y } }, { N_("Black"), "CMYK", FALSE, 1, FALSE, { CPN_CMYK_K } }, { 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} } }; static void decompose_class_init (DecomposeClass *klass) { PikaPlugInClass *plug_in_class = PIKA_PLUG_IN_CLASS (klass); plug_in_class->query_procedures = decompose_query_procedures; plug_in_class->create_procedure = decompose_create_procedure; plug_in_class->set_i18n = STD_SET_I18N; } static void decompose_init (Decompose *decompose) { } static GList * decompose_query_procedures (PikaPlugIn *plug_in) { return g_list_append (NULL, g_strdup (PLUG_IN_PROC)); } static PikaProcedure * decompose_create_procedure (PikaPlugIn *plug_in, const gchar *name) { PikaProcedure *procedure = NULL; if (! strcmp (name, PLUG_IN_PROC)) { GString *type_desc; gint i; 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, '"'); } procedure = pika_image_procedure_new (plug_in, name, PIKA_PDB_PROC_TYPE_PLUGIN, decompose_run, NULL, NULL); pika_procedure_set_image_types (procedure, "RGB*"); pika_procedure_set_sensitivity_mask (procedure, PIKA_PROCEDURE_SENSITIVE_DRAWABLE); pika_procedure_set_menu_label (procedure, _("_Decompose...")); pika_procedure_add_menu_path (procedure, "/Colors/Components"); pika_procedure_set_documentation (procedure, _("Decompose an image into separate " "colorspace components"), "This function creates new gray images " "with different channel information " "in each of them", name); pika_procedure_set_attribution (procedure, "Peter Kirchgessner", "Peter Kirchgessner, Clarence Risher", "1997"); PIKA_PROC_ARG_STRING (procedure, "decompose-type", "Decompose type", type_desc->str, "RGB", G_PARAM_READWRITE); PIKA_PROC_ARG_BOOLEAN (procedure, "layers-mode", "Layers mode", "Create channels as layers in a single image", TRUE, G_PARAM_READWRITE); PIKA_PROC_ARG_BOOLEAN (procedure, "use-registration", "Use registration", "When enabled, 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.", FALSE, G_PARAM_READWRITE); PIKA_PROC_VAL_IMAGE (procedure, "new-image-1", "New image 1", "Output gray image 1", FALSE, G_PARAM_READWRITE); PIKA_PROC_VAL_IMAGE (procedure, "new-image-2", "New image 2", "Output gray image 2 (N/A for single channel extract)", TRUE, G_PARAM_READWRITE); PIKA_PROC_VAL_IMAGE (procedure, "new-image-3", "New image 3", "Output gray image 3 (N/A for single channel extract)", TRUE, G_PARAM_READWRITE); PIKA_PROC_VAL_IMAGE (procedure, "new-image-4", "New image 4", "Output gray image 4 (N/A for single channel extract)", TRUE, G_PARAM_READWRITE); g_string_free (type_desc, TRUE); } return procedure; } static PikaValueArray * decompose_run (PikaProcedure *procedure, PikaRunMode run_mode, PikaImage *image, gint n_drawables, PikaDrawable **drawables, const PikaValueArray *args, gpointer run_data) { PikaProcedureConfig *config; PikaValueArray *return_vals; PikaDrawable *drawable; gint num_images; PikaImage *image_extract[MAX_EXTRACT_IMAGES]; PikaLayer *layer_extract[MAX_EXTRACT_IMAGES]; gint num_layers; GString *data; gchar *decompose_type; gchar *tmp; gint j; gegl_init (NULL, NULL); if (n_drawables != 1) { GError *error = NULL; g_set_error (&error, PIKA_PLUG_IN_ERROR, 0, _("Procedure '%s' only works with one drawable."), PLUG_IN_PROC); return pika_procedure_new_return_values (procedure, PIKA_PDB_CALLING_ERROR, error); } else { drawable = drawables[0]; } config = pika_procedure_create_config (procedure); pika_procedure_config_begin_run (config, NULL, run_mode, args); g_object_get (config, "decompose-type", &decompose_type, NULL); tmp = g_ascii_strup (decompose_type, -1); g_free (decompose_type); g_object_set (config, "decompose-type", tmp, NULL); g_free (tmp); if (run_mode == PIKA_RUN_INTERACTIVE) { if (! decompose_dialog (procedure, G_OBJECT (config))) { return pika_procedure_new_return_values (procedure, PIKA_PDB_CANCEL, NULL); } } pika_progress_init (_("Decomposing")); num_images = decompose (image, drawable, G_OBJECT (config), image_extract, &num_layers, layer_extract); if (num_images <= 0) { pika_procedure_config_end_run (config, PIKA_PDB_EXECUTION_ERROR); g_object_unref (config); return pika_procedure_new_return_values (procedure, PIKA_PDB_EXECUTION_ERROR, NULL); } /* create decompose-data parasite */ data = g_string_new (""); g_object_get (config, "decompose-type", &decompose_type, NULL); g_string_printf (data, "source=%d type=%s ", pika_item_get_id (PIKA_ITEM (drawable)), decompose_type); g_free (decompose_type); for (j = 0; j < num_layers; j++) g_string_append_printf (data, "%d ", pika_item_get_id (PIKA_ITEM (layer_extract[j]))); return_vals = pika_procedure_new_return_values (procedure, PIKA_PDB_SUCCESS, NULL); for (j = 0; j < num_images; j++) { PikaParasite *parasite; PIKA_VALUES_SET_IMAGE (return_vals, j + 1, image_extract[j]); pika_image_undo_enable (image_extract[j]); pika_image_clean_all (image_extract[j]); parasite = pika_parasite_new ("decompose-data", 0, data->len + 1, data->str); pika_image_attach_parasite (image_extract[j], parasite); pika_parasite_free (parasite); if (run_mode != PIKA_RUN_NONINTERACTIVE) pika_display_new (image_extract[j]); } pika_procedure_config_end_run (config, PIKA_PDB_SUCCESS); g_object_unref (config); pika_progress_end (); return return_vals; } /* Decompose an image. It returns the number of new (gray) images. * The image IDs for the new images are returned in image_dst. * On failure, -1 is returned. */ static gint decompose (PikaImage *image, PikaDrawable *drawable, GObject *config, PikaImage **image_dst, gint *nlayers, PikaLayer **layer_dst) { const gchar *layername; gint j, extract_idx; gint height, width; gint num_layers; GeglBuffer *src_buffer; GeglBuffer *dst_buffer[MAX_EXTRACT_IMAGES]; PikaPrecision precision; gboolean requirements = FALSE; gboolean decomp_has_alpha = FALSE; gchar *config_extract_type; gboolean config_as_layers; gboolean config_use_registration; g_object_get (config, "decompose-type", &config_extract_type, "layers-mode", &config_as_layers, "use-registration", &config_use_registration, NULL); extract_idx = -1; /* Search extract type */ for (j = 0; j < G_N_ELEMENTS (extract); j++) { if (g_ascii_strcasecmp (config_extract_type, extract[j].type) == 0) { extract_idx = j; break; } } if (extract_idx < 0) return -1; num_layers = extract[extract_idx].num_images; /* Sanity checks */ src_buffer = pika_drawable_get_buffer (drawable); precision = pika_image_get_precision (image); for (j = 0; j < num_layers; j++) { /* FIXME: Not 100% reliable */ 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); } requirements |= (pika_drawable_is_rgb (drawable)); requirements |= (pika_drawable_is_indexed (drawable)); requirements |= (pika_drawable_is_gray (drawable) && pika_drawable_has_alpha (drawable) && (num_layers <= 2) && decomp_has_alpha); requirements &= (!decomp_has_alpha || pika_drawable_has_alpha (drawable)); if (!requirements) { g_message (_("Image not suitable for this decomposition")); return -1; } width = gegl_buffer_get_width (src_buffer); height = gegl_buffer_get_height (src_buffer); /* Create all new gray images */ for (j = 0; j < num_layers; j++) { gchar *filename; gdouble xres, yres; filename = generate_filename (image, config, extract_idx, j); pika_image_get_resolution (image, &xres, &yres); if (config_as_layers) { layername = gettext (extract[extract_idx].component[j].channel_name); if (j == 0) image_dst[j] = create_new_image (g_file_new_for_path (filename), layername, width, height, PIKA_GRAY, precision, xres, yres, layer_dst + j); else layer_dst[j] = create_new_layer (image_dst[0], j, layername, width, height, PIKA_GRAY); } else { image_dst[j] = create_new_image (g_file_new_for_path (filename), NULL, width, height, PIKA_GRAY, precision, xres, yres, layer_dst + j); } g_free (filename); dst_buffer[j] = pika_drawable_get_buffer (PIKA_DRAWABLE (layer_dst[j])); } copy_n_components (src_buffer, dst_buffer, extract[extract_idx]); if (config_use_registration) transfer_registration_color (src_buffer, dst_buffer, num_layers); pika_progress_update (1.0); g_object_unref (src_buffer); for (j = 0; j < num_layers; j++) { g_object_unref (dst_buffer[j]); } *nlayers = num_layers; return (config_as_layers ? 1 : num_layers); } /* Create an image. Returns layer and image */ static PikaImage * create_new_image (GFile *file, const gchar *layername, guint width, guint height, PikaImageBaseType type, PikaPrecision precision, gdouble xres, gdouble yres, PikaLayer **layer) { PikaImage *image; image = pika_image_new_with_precision (width, height, type, precision); pika_image_undo_disable (image); pika_image_set_file (image, file); pika_image_set_resolution (image, xres, yres); *layer = create_new_layer (image, 0, layername, width, height, type); return image; } static PikaLayer * create_new_layer (PikaImage *image, gint position, const gchar *layername, guint width, guint height, PikaImageBaseType type) { PikaLayer *layer; PikaImageType gdtype = PIKA_RGB_IMAGE; switch (type) { case PIKA_RGB: gdtype = PIKA_RGB_IMAGE; break; case PIKA_GRAY: gdtype = PIKA_GRAY_IMAGE; break; case PIKA_INDEXED: gdtype = PIKA_INDEXED_IMAGE; break; } if (! layername) layername = _("Background"); layer = pika_layer_new (image, layername, width, height, gdtype, 100, pika_image_get_default_new_layer_mode (image)); pika_image_insert_layer (image, layer, NULL, position); return layer; } /* Registration Color function */ static void transfer_registration_color (GeglBuffer *src, GeglBuffer **dst, gint count) { PikaRGB color, test; GeglBufferIterator *gi; const Babl *src_format; const Babl *dst_format; gint src_bpp; gint dst_bpp; gint i; gdouble white; pika_context_get_foreground (&color); white = 1.0; src_format = gegl_buffer_get_format (src); src_bpp = babl_format_get_bytes_per_pixel (src_format); dst_format = gegl_buffer_get_format (dst[0]); dst_bpp = babl_format_get_bytes_per_pixel (dst_format); gi = gegl_buffer_iterator_new (src, NULL, 0, NULL, GEGL_ACCESS_READ, GEGL_ABYSS_NONE, 10); for (i = 0; i < count; i++) { gegl_buffer_iterator_add (gi, dst[i], NULL, 0, NULL, GEGL_ACCESS_READWRITE, GEGL_ABYSS_NONE); } while (gegl_buffer_iterator_next (gi)) { gpointer src_data; gpointer dst_data[MAX_EXTRACT_IMAGES]; gint j, k; src_data = gi->items[0].data; for (j = 0; j < count; j++) dst_data[j] = gi->items[j + 1].data; for (k = 0; k < gi->length; k++) { gulong pos = k * src_bpp; pika_rgba_set_pixel (&test, src_format, ((guchar *)src_data) + pos); if (pika_rgb_distance (&test, &color) < 1e-6) { for (j = 0; j < count; j++) { gpointer data = dst_data[j]; babl_process (babl_fish (babl_format ("Y double"), dst_format), &white, (guchar *)data + (k * dst_bpp), 1); } } } } } static void cpn_affine_transform_clamp (GeglBuffer *buffer, gdouble min, gdouble max, gboolean clamp) { GeglBufferIterator *gi; gdouble scale = 1.0 / (max - min); gdouble offset = - min; /* 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, GEGL_ACCESS_READWRITE, GEGL_ABYSS_NONE, 1); while (gegl_buffer_iterator_next (gi)) { guint k; double *data; data = (double*) gi->items[0].data; if (clamp) { for (k = 0; k < gi->length; k++) { data[k] = CLAMP ((data[k] + offset) * scale, 0.0, 1.0); } } else { for (k = 0; k < gi->length; k++) { data[k] = (data[k] + offset) * scale; } } } } static void copy_n_components (GeglBuffer *src, GeglBuffer **dst, Extract ext) { gint i; for (i = 0; i < ext.num_images; i++) { pika_progress_update ((gdouble) i / (gdouble) ext.num_images); copy_one_component (src, dst[i], ext.model, ext.component[i], ext.clamp); } } static void copy_one_component (GeglBuffer *src, GeglBuffer *dst, const gchar *model, const Component component, gboolean clamp) { const Babl *component_format; const Babl *dst_format; GeglBuffer *temp; const GeglRectangle *extent; /* We are working in linear double precision */ component_format = babl_format_new (babl_model (model), babl_type ("double"), babl_component (component.babl_name), NULL); /* We need to enforce linearity here * If the output is "Y'", the output of temp is already ok * If the output is "Y" , it will enforce gamma-decoding. * A bit tricky and suboptimal... */ if (component.perceptual_channel) dst_format = babl_format ("Y' double"); else dst_format = babl_format ("Y double"); extent = gegl_buffer_get_extent (src); temp = gegl_buffer_new (extent, dst_format); /* we want to copy the component as is */ gegl_buffer_set_format (temp, component_format); gegl_buffer_copy (src, NULL, GEGL_ABYSS_NONE, temp, NULL); if (component.range_min != 0.0 || component.range_max != 1.0 || clamp) { cpn_affine_transform_clamp (temp, component.range_min, component.range_max, clamp); } /* This is our new "Y(') double" component buffer */ gegl_buffer_set_format (temp, NULL); /* Now we let babl convert it back to the format that dst needs */ gegl_buffer_copy (temp, NULL, GEGL_ABYSS_NONE, dst, NULL); g_object_unref (temp); } static gboolean decompose_dialog (PikaProcedure *procedure, GObject *config) { GtkWidget *dialog; GtkWidget *main_vbox; GtkWidget *frame; GtkWidget *vbox; GtkWidget *hbox; GtkWidget *label; GtkListStore *store; GtkWidget *combo; GtkWidget *toggle; gchar *config_extract_type; gint j; gboolean run; g_object_get (config, "decompose-type", &config_extract_type, NULL); pika_ui_init (PLUG_IN_BINARY); dialog = pika_procedure_dialog_new (procedure, PIKA_PROCEDURE_CONFIG (config), _("Decompose")); gtk_window_set_resizable (GTK_WINDOW (dialog), FALSE); main_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12); gtk_container_set_border_width (GTK_CONTAINER (main_vbox), 12); gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))), main_vbox, TRUE, TRUE, 0); gtk_widget_show (main_vbox); frame = pika_frame_new (_("Extract Channels")); gtk_box_pack_start (GTK_BOX (main_vbox), frame, TRUE, TRUE, 0); gtk_widget_show (frame); vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6); gtk_container_add (GTK_CONTAINER (frame), vbox); gtk_widget_show (vbox); hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6); gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0); gtk_widget_show (hbox); label = gtk_label_new_with_mnemonic (_("Color _model:")); gtk_label_set_xalign (GTK_LABEL (label), 0.0); gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0); gtk_widget_show (label); store = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_STRING); for (j = 0; j < G_N_ELEMENTS (extract); j++) { if (extract[j].dialog) { gchar *label = g_strdup (gettext (extract[j].type)); gchar *l; for (l = label; *l; l++) if (*l == '-' || *l == '_') *l = ' '; gtk_list_store_insert_with_values (store, NULL, G_N_ELEMENTS (extract), 0, extract[j].type, 1, label, -1); g_free (label); } } combo = pika_prop_string_combo_box_new (config, "decompose-type", GTK_TREE_MODEL (store), 0, 1); gtk_box_pack_start (GTK_BOX (hbox), combo, TRUE, TRUE, 0); g_object_unref (store); gtk_label_set_mnemonic_widget (GTK_LABEL (label), combo); toggle = pika_prop_check_button_new (config, "layers-mode", _("_Decompose to layers")); gtk_box_pack_start (GTK_BOX (main_vbox), toggle, FALSE, FALSE, 0); toggle = pika_prop_check_button_new (config, "use-registration", _("_Foreground as registration color")); pika_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_box_pack_start (GTK_BOX (main_vbox), toggle, FALSE, FALSE, 0); gtk_widget_show (dialog); run = pika_procedure_dialog_run (PIKA_PROCEDURE_DIALOG (dialog)); gtk_widget_destroy (dialog); return run; } /* Build a filename like -. */ gchar * generate_filename (PikaImage *image, GObject *config, guint colorspace, guint channel) { /* Build a filename like -. */ gchar *fname; gchar *filename; gchar *extension; gboolean config_as_layers; g_object_get (config, "layers-mode", &config_as_layers, NULL); fname = g_file_get_path (pika_image_get_file (image)); if (fname) { extension = fname + strlen (fname) - 1; while (extension >= fname) { if (*extension == '.') break; extension--; } if (extension >= fname) { *(extension++) = '\0'; if (config_as_layers) filename = g_strdup_printf ("%s-%s.%s", fname, gettext (extract[colorspace].type), extension); else filename = g_strdup_printf ("%s-%s.%s", fname, gettext (extract[colorspace].component[channel].channel_name), extension); } else { if (config_as_layers) filename = g_strdup_printf ("%s-%s", fname, gettext (extract[colorspace].type)); else filename = g_strdup_printf ("%s-%s", fname, gettext (extract[colorspace].component[channel].channel_name)); } } else { filename = g_strdup (gettext (extract[colorspace].component[channel].channel_name)); } g_free (fname); return filename; }