/* GIMP - The GNU Image Manipulation Program * 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 "libgimp/stdplugins-intl.h" #define PLUG_IN_PROC "plug-in-decompose" #define PLUG_IN_BINARY "decompose" #define PLUG_IN_ROLE "gimp-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 { GimpPlugIn parent_instance; }; struct _DecomposeClass { GimpPlugInClass 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 (GimpPlugIn *plug_in); static GimpProcedure * decompose_create_procedure (GimpPlugIn *plug_in, const gchar *name); static GimpValueArray * decompose_run (GimpProcedure *procedure, GimpRunMode run_mode, GimpImage *image, GimpDrawable **drawables, GimpProcedureConfig *config, gpointer run_data); static gint decompose (GimpImage *image, GimpDrawable *drawable, GObject *config, GimpImage **image_dst, gint32 *num_layers, GimpLayer **layer_dst); static GimpImage * create_new_image (GFile *file, const gchar *layername, guint width, guint height, GimpImageBaseType type, GimpPrecision precision, gdouble xres, gdouble yres, GimpLayer **layer); static GimpLayer * create_new_layer (GimpImage *image, gint position, const gchar *layername, guint width, guint height, GimpImageBaseType 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 (GimpProcedure *procedure, GObject *config); static gchar * generate_filename (GimpImage *image, GObject *config, guint colorspace, guint channel); G_DEFINE_TYPE (Decompose, decompose, GIMP_TYPE_PLUG_IN) GIMP_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_("YCbCr470"), "Y'CbCr", TRUE, 3, FALSE, { CPN_YCBCR_Y, CPN_YCBCR_CB, CPN_YCBCR_CR} }, { N_("YCbCr470f"), "Y'CbCr", TRUE, 3, TRUE, { CPN_YCBCR_Y, CPN_YCBCR_CB, CPN_YCBCR_CR} }, { N_("YCbCr709"), "Y'CbCr709", TRUE, 3, FALSE, { CPN_YCBCR709_Y, CPN_YCBCR709_CB, CPN_YCBCR709_CR} }, { N_("YCbCr709f"), "Y'CbCr709", TRUE, 3, TRUE, { CPN_YCBCR709_Y, CPN_YCBCR709_CB, CPN_YCBCR709_CR} } }; static void decompose_class_init (DecomposeClass *klass) { GimpPlugInClass *plug_in_class = GIMP_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 (GimpPlugIn *plug_in) { return g_list_append (NULL, g_strdup (PLUG_IN_PROC)); } static GimpProcedure * decompose_create_procedure (GimpPlugIn *plug_in, const gchar *name) { GimpProcedure *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 = gimp_image_procedure_new (plug_in, name, GIMP_PDB_PROC_TYPE_PLUGIN, decompose_run, NULL, NULL); gimp_procedure_set_image_types (procedure, "RGB*"); gimp_procedure_set_sensitivity_mask (procedure, GIMP_PROCEDURE_SENSITIVE_DRAWABLE); gimp_procedure_set_menu_label (procedure, _("_Decompose...")); gimp_procedure_add_menu_path (procedure, "/Colors/Components"); gimp_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); gimp_procedure_set_attribution (procedure, "Peter Kirchgessner", "Peter Kirchgessner, Clarence Risher", "1997"); gimp_procedure_add_choice_argument (procedure, "decompose-type", _("Color _model"), _("The model to decompose to"), gimp_choice_new_with_values ("rgb", 0, _("RGB"), NULL, "rgba", 1, _("RGBA"), NULL, "alpha", 2, _("Alpha"), NULL, "hsv", 3, _("HSV"), NULL, "hsl", 4, _("HSL"), NULL, "cmyk", 5, _("CMYK"), NULL, "lab", 6, _("LAB"), NULL, "lch", 7, _("LCH"), NULL, "ycbcr470", 8, _("YCbCr ITU R470"), NULL, "ycbcr709", 9, _("YCbCr ITU R709"), NULL, "ycbcr470f", 10, _("YCbCr ITU R470 256"), NULL, "ycbcr709f", 11, _("YCbCr ITU R709 256"), NULL, NULL), "rgb", G_PARAM_READWRITE); gimp_procedure_add_boolean_argument (procedure, "layers-mode", _("_Decompose to layers"), _("Create channels as layers in a single image"), TRUE, G_PARAM_READWRITE); gimp_procedure_add_boolean_argument (procedure, "use-registration", _("_Foreground as registration color"), _("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); gimp_procedure_add_image_return_value (procedure, "new-image-1", "New image 1", "Output gray image 1", FALSE, G_PARAM_READWRITE); gimp_procedure_add_image_return_value (procedure, "new-image-2", "New image 2", "Output gray image 2 (N/A for single channel extract)", TRUE, G_PARAM_READWRITE); gimp_procedure_add_image_return_value (procedure, "new-image-3", "New image 3", "Output gray image 3 (N/A for single channel extract)", TRUE, G_PARAM_READWRITE); gimp_procedure_add_image_return_value (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 GimpValueArray * decompose_run (GimpProcedure *procedure, GimpRunMode run_mode, GimpImage *image, GimpDrawable **drawables, GimpProcedureConfig *config, gpointer run_data) { GimpValueArray *return_vals; GimpDrawable *drawable; gint num_images; GimpImage *image_extract[MAX_EXTRACT_IMAGES]; GimpLayer *layer_extract[MAX_EXTRACT_IMAGES]; gint num_layers; GString *data; gchar *decompose_type; gint j; gegl_init (NULL, NULL); if (gimp_core_object_array_get_length ((GObject **) drawables) != 1) { GError *error = NULL; g_set_error (&error, GIMP_PLUG_IN_ERROR, 0, _("Procedure '%s' only works with one drawable."), PLUG_IN_PROC); return gimp_procedure_new_return_values (procedure, GIMP_PDB_CALLING_ERROR, error); } else { drawable = drawables[0]; } if (run_mode == GIMP_RUN_INTERACTIVE && ! decompose_dialog (procedure, G_OBJECT (config))) return gimp_procedure_new_return_values (procedure, GIMP_PDB_CANCEL, NULL); gimp_progress_init (_("Decomposing")); num_images = decompose (image, drawable, G_OBJECT (config), image_extract, &num_layers, layer_extract); if (num_images <= 0) return gimp_procedure_new_return_values (procedure, GIMP_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 ", gimp_item_get_id (GIMP_ITEM (drawable)), decompose_type); g_free (decompose_type); for (j = 0; j < num_layers; j++) g_string_append_printf (data, "%d ", gimp_item_get_id (GIMP_ITEM (layer_extract[j]))); return_vals = gimp_procedure_new_return_values (procedure, GIMP_PDB_SUCCESS, NULL); for (j = 0; j < num_images; j++) { GimpParasite *parasite; GIMP_VALUES_SET_IMAGE (return_vals, j + 1, image_extract[j]); gimp_image_undo_enable (image_extract[j]); gimp_image_clean_all (image_extract[j]); parasite = gimp_parasite_new ("decompose-data", 0, data->len + 1, data->str); gimp_image_attach_parasite (image_extract[j], parasite); gimp_parasite_free (parasite); if (run_mode != GIMP_RUN_NONINTERACTIVE) gimp_display_new (image_extract[j]); } gimp_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 (GimpImage *image, GimpDrawable *drawable, GObject *config, GimpImage **image_dst, gint *nlayers, GimpLayer **layer_dst) { const gchar *layername; gint j, extract_idx; gint height, width; gint num_layers; GeglBuffer *src_buffer; GeglBuffer *dst_buffer[MAX_EXTRACT_IMAGES]; GimpPrecision 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 = gimp_drawable_get_buffer (drawable); precision = gimp_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 |= (gimp_drawable_is_rgb (drawable)); requirements |= (gimp_drawable_is_indexed (drawable)); requirements |= (gimp_drawable_is_gray (drawable) && gimp_drawable_has_alpha (drawable) && (num_layers <= 2) && decomp_has_alpha); requirements &= (!decomp_has_alpha || gimp_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); gimp_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, GIMP_GRAY, precision, xres, yres, layer_dst + j); else layer_dst[j] = create_new_layer (image_dst[0], j, layername, width, height, GIMP_GRAY); } else { image_dst[j] = create_new_image (g_file_new_for_path (filename), NULL, width, height, GIMP_GRAY, precision, xres, yres, layer_dst + j); } g_free (filename); dst_buffer[j] = gimp_drawable_get_buffer (GIMP_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); gimp_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 GimpImage * create_new_image (GFile *file, const gchar *layername, guint width, guint height, GimpImageBaseType type, GimpPrecision precision, gdouble xres, gdouble yres, GimpLayer **layer) { GimpImage *image; image = gimp_image_new_with_precision (width, height, type, precision); gimp_image_undo_disable (image); gimp_image_set_file (image, file); gimp_image_set_resolution (image, xres, yres); *layer = create_new_layer (image, 0, layername, width, height, type); return image; } static GimpLayer * create_new_layer (GimpImage *image, gint position, const gchar *layername, guint width, guint height, GimpImageBaseType type) { GimpLayer *layer; GimpImageType gdtype = GIMP_RGB_IMAGE; switch (type) { case GIMP_RGB: gdtype = GIMP_RGB_IMAGE; break; case GIMP_GRAY: gdtype = GIMP_GRAY_IMAGE; break; case GIMP_INDEXED: gdtype = GIMP_INDEXED_IMAGE; break; } if (! layername) layername = _("Background"); layer = gimp_layer_new (image, layername, width, height, gdtype, 100, gimp_image_get_default_new_layer_mode (image)); gimp_image_insert_layer (image, layer, NULL, position); return layer; } /* Registration Color function */ static void transfer_registration_color (GeglBuffer *src, GeglBuffer **dst, gint count) { GeglColor *color; GeglBufferIterator *gi; const Babl *src_format; const Babl *dst_format; gint src_bpp; gint dst_bpp; gint i; gdouble white; color = gimp_context_get_foreground (); 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++) { GeglColor *test; gulong pos = k * src_bpp; test = gegl_color_new (NULL); gegl_color_set_pixel (test, src_format, ((guchar *)src_data) + pos); if (gimp_color_is_perceptually_identical (test, color)) { 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); } } g_object_unref (test); } } g_object_unref (color); } 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++) { gimp_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 (GimpProcedure *procedure, GObject *config) { GtkWidget *dialog; GtkWidget *vbox; gboolean run; gimp_ui_init (PLUG_IN_BINARY); dialog = gimp_procedure_dialog_new (procedure, GIMP_PROCEDURE_CONFIG (config), _("Decompose")); gtk_window_set_resizable (GTK_WINDOW (dialog), FALSE); gimp_procedure_dialog_get_label (GIMP_PROCEDURE_DIALOG (dialog), "extract-label", _("Extract Channels"), FALSE, FALSE); gimp_procedure_dialog_fill_frame (GIMP_PROCEDURE_DIALOG (dialog), "extract-frame", "extract-label", FALSE, "decompose-type"); vbox = gimp_procedure_dialog_fill_box (GIMP_PROCEDURE_DIALOG (dialog), "decompose-vbox", "extract-frame", "layers-mode", "use-registration", NULL); gtk_box_set_spacing (GTK_BOX (vbox), 12); gtk_container_set_border_width (GTK_CONTAINER (vbox), 12); gimp_procedure_dialog_fill (GIMP_PROCEDURE_DIALOG (dialog), "decompose-vbox", NULL); gtk_widget_set_visible (dialog, TRUE); run = gimp_procedure_dialog_run (GIMP_PROCEDURE_DIALOG (dialog)); gtk_widget_destroy (dialog); return run; } /* Build a filename like -. */ gchar * generate_filename (GimpImage *image, GObject *config, guint colorspace, guint channel) { /* Build a filename like -. */ GFile *file; gchar *fname = NULL; gchar *filename; gchar *extension; gboolean config_as_layers; g_object_get (config, "layers-mode", &config_as_layers, NULL); file = gimp_image_get_file (image); if (file) fname = g_file_get_path (file); 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.xcf", fname, gettext (extract[colorspace].type)); else filename = g_strdup_printf ("%s-%s.xcf", fname, gettext (extract[colorspace].component[channel].channel_name)); } else { if (config_as_layers) filename = g_strdup_printf ("%s.xcf", gettext (extract[colorspace].type)); else filename = g_strdup_printf ("%s.xcf", gettext (extract[colorspace].component[channel].channel_name)); } g_free (fname); return filename; }