[gimp] plugins: finish the port of decompose.c



commit 385a6b60f3040eeeda6abc3525d70fc98e5addcc
Author: Téo Mazars <teo mazars ensimag fr>
Date:   Sun Jun 30 20:28:38 2013 +0200

    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 |  266 +++++++++++++++++++++++++++++--------------
 1 files changed, 179 insertions(+), 87 deletions(-)
---
diff --git a/plug-ins/common/decompose.c b/plug-ins/common/decompose.c
index f67412f..9217d3d 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 <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_("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_("RGBA"), "RGBA",    TRUE,  4, { "R", "G", "B", "A" },               { N_("red"),
-                                                                               N_("green"),
-                                                                               N_("blue"),
-                                                                               N_("alpha") } },
-
-    { N_("LAB"),  "CIE Lab", FALSE,  3, {"CIE L", "CIE a", "CIE b"},         { N_("L"),
-                                                                               N_("A"),
-                                                                               N_("B") } },
-
-    { N_("CMYK"), "CMYK",   FALSE,  4, {"cyan", "magenta", "yellow", "key"}, { N_("cyan-k"),
-                                                                               N_("magenta-k"),
-                                                                               N_("yellow-k"),
-                                                                               N_("black") } },
-
-    { N_("HSVA"), "HSVA", FALSE, 4, {"hue", "saturation", "value", "alpha"}, { N_("hue"),
-                                                                               N_("saturation"),
-                                                                               N_("value")} }
+    { 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_("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", 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_("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)
+{
+  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_BUFFER_READWRITE, GEGL_ABYSS_NONE);
+
+  while (gegl_buffer_iterator_next (gi))
+    {
+      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_n_components (GeglBuffer      *src,
+                   GeglBuffer     **dst,
+                   EXTRACT          ext)
 {
   gint i;
 
-  for (i = 0; i < n; i++)
+  for (i = 0; i < ext.num_images; i++)
     {
-      gimp_progress_update ((gdouble) i / (gdouble) n);
-      copy_one_component (src, dst[i], model, components[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 gchar *component)
+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;


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]