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:
Téo Mazars 2013-06-30 20:28:38 +02:00
parent 0fc78cd811
commit 385a6b60f3
1 changed files with 175 additions and 83 deletions

View File

@ -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;