From 385a6b60f3040eeeda6abc3525d70fc98e5addcc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?T=C3=A9o=20Mazars?= Date: Sun, 30 Jun 2013 20:28:38 +0200 Subject: [PATCH] plugins: finish the port of decompose.c - Add all missing decompositions needed for compat - There are still some noticeable differences with the old plugin (YCbCr mainly) - decomposition of alpha is not coherent with gimp's current behaviour. It still needs to be discussed. - clamping is only here for compat, but it's probably not really needed. - Others decompositions can now easily be added. - compose.c remains unported --- plug-ins/common/decompose.c | 258 ++++++++++++++++++++++++------------ 1 file changed, 175 insertions(+), 83 deletions(-) diff --git a/plug-ins/common/decompose.c b/plug-ins/common/decompose.c index f67412f4b3..9217d3d905 100644 --- a/plug-ins/common/decompose.c +++ b/plug-ins/common/decompose.c @@ -4,6 +4,9 @@ * 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 @@ -18,10 +21,6 @@ * along with this program. If not, see . */ -/* - * This filter decomposes RGB-images into several types of channels - */ - /* Lab colorspace support originally written by Alexey Dyachenko, * merged into the officical plug-in by Sven Neumann. */ @@ -42,6 +41,52 @@ #define PLUG_IN_ROLE "gimp-decompose" +/* Descrition of a component */ +typedef struct +{ + /* the babl_component names of the channels */ + const gchar *babl_name; + + /* Names of channels to extract */ + const gchar *channel_name; + + /* min and max */ + const gdouble range_min; + const gdouble range_max; + + /* Make the channel "correct" in Y' space */ + const gboolean perceptual_channel; + +} 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; /* Dialog-Flag. Set it to TRUE if you want to appear + * this extract function within 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 +{ + gchar extract_type[32]; + gboolean as_layers; + gboolean use_registration; +} DecoVals; + + /* Declare local functions */ static void query (void); @@ -74,80 +119,96 @@ static gint32 create_new_layer (gint32 image_ID, static void transfer_registration_color (GeglBuffer *src, GeglBuffer **dst, gint count); +static void cpn_affine_transform_clamp (GeglBuffer *buffer, + gdouble min, + gdouble max); static void copy_n_components (GeglBuffer *src, GeglBuffer **dst, - const gchar *model, - guint n, - const gchar **components); + EXTRACT ext); static void copy_one_component (GeglBuffer *src, GeglBuffer *dst, const char *model, - const char *component); + const COMPONENT component, + gboolean clamp); static gboolean decompose_dialog (void); static gchar * generate_filename (guint32 image_ID, guint colorspace, guint channel); -/* Maximum number of images/layers generated by an extraction */ -#define MAX_EXTRACT_IMAGES 4 +#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} -/* Description of an extraction */ +#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} -typedef struct -{ - const gchar *type; /* What to extract */ - const gchar *model ; /* the babl_model string to use */ - gboolean dialog; /* Dialog-Flag. Set it to TRUE if you want to appear - * this extract function within the dialog */ - gint num_images; /* Number of images to create */ +#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_("value"), 0.0, 1.0, TRUE} - /* the babl_component names of the channels */ - const gchar *component[MAX_EXTRACT_IMAGES]; +#define CPN_CMYK_C {"cyan", N_("cyan-k"), 0.0, 1.0, TRUE} +#define CPN_CMYK_M {"magenta", N_("magenta-k"), 0.0, 1.0, TRUE} +#define CPN_CMYK_Y {"yellow", N_("yellow-k"), 0.0, 1.0, TRUE} +#define CPN_CMYK_K {"key", N_("black"), 0.0, 1.0, TRUE} - /* Names of channels to extract */ - const gchar *channel_name[MAX_EXTRACT_IMAGES]; +#define CPN_CMY_C {"cyan", N_("cyan"), 0.0, 1.0, TRUE} +#define CPN_CMY_M {"magenta", N_("magenta"), 0.0, 1.0, TRUE} +#define CPN_CMY_Y {"yellow", N_("yellow"), 0.0, 1.0, TRUE} -} EXTRACT; +#define CPN_LAB_L {"CIE L", N_("L"), 0.0, 100.0, TRUE} +#define CPN_LAB_A {"CIE a", N_("A"), -128.0, 127.0, TRUE} +#define CPN_LAB_B {"CIE b", N_("B"), -128.0, 127.0, TRUE} -/* FIXME: lots of missing conversions here */ -static EXTRACT extract[] = +#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, {"R", "G", "B"}, { N_("red"), - N_("green"), - N_("blue") } }, + { 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_("Red"), "RGB", FALSE, 1, { "R" }, { N_("red") } }, - { N_("Green"), "RGB", FALSE, 1, { "G" }, { N_("green") } }, - { N_("Blue"), "RGB", FALSE, 1, { "B" }, { N_("blue") } }, - { N_("Alpha"), "RGBA", TRUE , 1, { "A" }, { N_("alpha") } }, + { 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_("RGBA"), "RGBA", TRUE, 4, { "R", "G", "B", "A" }, { N_("red"), - N_("green"), - N_("blue"), - N_("alpha") } }, + { 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_("LAB"), "CIE Lab", FALSE, 3, {"CIE L", "CIE a", "CIE b"}, { N_("L"), - N_("A"), - N_("B") } }, + { N_("CMY"), "CMY", TRUE, 3, FALSE, {CPN_CMY_C, CPN_CMY_M, CPN_CMY_Y} }, + { N_("Cyan"), "CMY", FALSE, 1, FALSE, {CPN_CMY_C} }, + { N_("Magenta"), "CMY", FALSE, 1, FALSE, {CPN_CMY_M} }, + { N_("Yeallow"), "CMY", FALSE, 1, FALSE, {CPN_CMY_Y} }, - { N_("CMYK"), "CMYK", FALSE, 4, {"cyan", "magenta", "yellow", "key"}, { N_("cyan-k"), - N_("magenta-k"), - N_("yellow-k"), - N_("black") } }, + { N_("CMYK"), "CMYK", TRUE, 4, FALSE, {CPN_CMYK_C, CPN_CMYK_M, CPN_CMYK_Y, CPN_CMYK_K} }, + { N_("Cyan_K"), "CMYK", FALSE, 1, FALSE, {CPN_CMYK_C} }, + { N_("Magenta_K"), "CMYK", FALSE, 1, FALSE, {CPN_CMYK_M} }, + { N_("Yellow_K"), "CMYK", FALSE, 1, FALSE, {CPN_CMYK_Y} }, - { N_("HSVA"), "HSVA", FALSE, 4, {"hue", "saturation", "value", "alpha"}, { N_("hue"), - N_("saturation"), - N_("value")} } + { N_("LAB"), "CIE Lab", TRUE, 3, FALSE, {CPN_LAB_L, CPN_LAB_A, CPN_LAB_B} }, + + { 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} } }; -typedef struct -{ - gchar extract_type[32]; - gboolean as_layers; - gboolean use_registration; -} DecoVals; - const GimpPlugInInfo PLUG_IN_INFO = { NULL, /* init_proc */ @@ -408,10 +469,11 @@ decompose (gint32 image_ID, src_buffer = gimp_drawable_get_buffer (drawable_ID); precision = gimp_image_get_precision (image_ID); - for(j = 0; j < num_layers; j++) + for (j = 0; j < num_layers; j++) { - decomp_has_alpha |= !g_strcmp0 ("alpha", extract[extract_idx].component[j]); - decomp_has_alpha |= !g_strcmp0 ("A", extract[extract_idx].component[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); } requirments |= (gimp_drawable_is_rgb (drawable_ID)); @@ -441,7 +503,7 @@ decompose (gint32 image_ID, if (decovals.as_layers) { - layername = gettext (extract[extract_idx].channel_name[j]); + layername = gettext (extract[extract_idx].component[j].channel_name); if (j == 0) image_ID_dst[j] = create_new_image (filename, layername, @@ -466,9 +528,7 @@ decompose (gint32 image_ID, } copy_n_components (src_buffer, dst_buffer, - extract[extract_idx].model, - extract[extract_idx].num_images, - extract[extract_idx].component); + extract[extract_idx]); if (decovals.use_registration) transfer_registration_color (src_buffer, dst_buffer, num_layers); @@ -610,26 +670,55 @@ transfer_registration_color (GeglBuffer *src, } static void -copy_n_components (GeglBuffer *src, - GeglBuffer **dst, - const gchar *model, - guint n, - const gchar **components) +cpn_affine_transform_clamp (GeglBuffer *buffer, + gdouble min, + gdouble max) { - gint i; + GeglBufferIterator *gi; - for (i = 0; i < n; i++) + 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_BUFFER_READWRITE, GEGL_ABYSS_NONE); + + while (gegl_buffer_iterator_next (gi)) { - gimp_progress_update ((gdouble) i / (gdouble) n); - copy_one_component (src, dst[i], model, components[i]); + guint k; + double *data; + + data = (double*) gi->data[0]; + + for (k = 0; k < gi->length; k++) + { + data[k] = CLAMP ((data[k] + offset) * scale, 0.0, 1.0); + } } } static void -copy_one_component (GeglBuffer *src, - GeglBuffer *dst, - const gchar *model, - const gchar *component) +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, *dst_format; GeglBuffer *temp; @@ -638,15 +727,15 @@ copy_one_component (GeglBuffer *src, /* We are working in linear double precison*/ component_format = babl_format_new (babl_model (model), babl_type ("double"), - babl_component (component), + babl_component (component.babl_name), NULL); - /* Alpha case, we need to enforce linearity here + /* We need to enforce linearity here * If the output is "Y'", the ouput of temp is already ok - * If the output is "Y" , it will enforce gamma-decoding... - * A bit tricky... + * If the output is "Y" , it will enforce gamma-decoding. + * A bit tricky and suboptimal... */ - if (!g_strcmp0 (component , "A")) + if (component.perceptual_channel) dst_format = babl_format ("Y' double"); else dst_format = babl_format ("Y double"); @@ -654,11 +743,14 @@ copy_one_component (GeglBuffer *src, extent = gegl_buffer_get_extent (src); temp = gegl_buffer_new (extent, dst_format); - /* we want to copy the componnent as is */ + /* we want to copy the component as is */ gegl_buffer_set_format (temp, component_format); gegl_buffer_copy (src, NULL, temp, NULL); - /* This is our new "Y(') double" componnent buffer */ + if (component.range_min != 0.0 || component.range_max != 1.0 || clamp) + cpn_affine_transform_clamp (temp, component.range_min, component.range_max); + + /* 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 */ @@ -833,7 +925,7 @@ generate_filename (guint32 image_ID, guint colorspace, guint channel) extension); else filename = g_strdup_printf ("%s-%s.%s", fname, - gettext (extract[colorspace].channel_name[channel]), + gettext (extract[colorspace].component[channel].channel_name), extension); } else @@ -843,12 +935,12 @@ generate_filename (guint32 image_ID, guint colorspace, guint channel) gettext (extract[colorspace].type)); else filename = g_strdup_printf ("%s-%s", fname, - gettext (extract[colorspace].channel_name[channel])); + gettext (extract[colorspace].component[channel].channel_name)); } } else { - filename = g_strdup (gettext (extract[colorspace].channel_name[channel])); + filename = g_strdup (gettext (extract[colorspace].component[channel].channel_name)); } g_free (fname); return filename;