mirror of https://github.com/GNOME/gimp.git
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
This commit is contained in:
parent
0fc78cd811
commit
385a6b60f3
|
@ -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 <mail_dev@martijn.at>
|
||||
* Copyright 2013 Téo Mazars <teo.mazars@ensimag.fr>
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/*
|
||||
* 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;
|
||||
|
|
Loading…
Reference in New Issue