PATCH: Add HSL to compose/decompose plugins

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

 



The attached patch (against 2.3.13) adds support for the HSL color
space to compose/decompose.  In my experience, HSL is often a better
color space for manipulation than HSV; it better reflects our
perception than HSV.  An example is a photo I'm currently working on
of a red leaf backlit against a blue sky; I'm trying to balance the
leaf against the sky.  The leaf is perceptually considerably darker
than the sky (which is reflected well in luminosity), but the values
of the leaf and the sky are very similar, making it hard to correct
via curves.  As a result, this is not difficult to correct in HSL
space, but is very difficult to enhance in HSV.

Note that the definition of saturation is a bit different in HSL
vs. HSV.

In Gutenprint we use another color space for corrections, HLK
(hue/luminosity/black).  K is defined in the usual way of min(C,M,Y)
(white would be defined as min(R,G,B)).  The H and L components are
then computed from the residual; S is implicitly 1 since min is zero
(and L is never greater than 0.5, for the same reason, so a reasonable
HLK space would double the L value).  This is more useful for CMYK
printing than it likely is for RGB image manipulation; it allows us to
correct the properties of the color inks without affecting the black
generation, as would otherwise happen if we were to use HSL space (and
it also simplifies the color correction since we don't have to worry
about saturation).  This was one of the major advances in color
correction we made in 5.0.  I'd be happy to code it up if people are
interested.
--- ./libgimpcolor/gimpcolorspace.c~	2006-06-27 06:33:48.000000000 -0400
+++ ./libgimpcolor/gimpcolorspace.c	2007-01-06 13:07:51.000000000 -0500
@@ -1003,6 +1003,85 @@
 }
 
 /**
+ * gimp_rgb_to_hsl4:
+ * @rgb:        RGB triplet, rgb[0] is red channel, rgb[1] is green,
+ *              rgb[2] is blue (0..255)
+ * @hue:        Pointer to hue channel (0..1)
+ * @saturation: Pointer to saturation channel (0..1)
+ * @luma:       Pointer to luma channel (0..1)
+ **/
+void
+gimp_rgb_to_hsl4 (const guchar *rgb,
+                  gdouble      *hue,
+                  gdouble      *saturation,
+                  gdouble      *luma)
+{
+  gdouble red, green, blue;
+  gdouble h, s, l;
+  gdouble min, max;
+  gdouble delta;
+
+  red   = rgb[0] / 255.0;
+  green = rgb[1] / 255.0;
+  blue  = rgb[2] / 255.0;
+
+  h = 0.0; /* Shut up -Wall */
+
+  if (red > green)
+    {
+      max = MAX (red,   blue);
+      min = MIN (green, blue);
+    }
+  else
+    {
+      max = MAX (green, blue);
+      min = MIN (red,   blue);
+    }
+
+  l = (max + min) / 2.0;
+
+  if (max == min)
+    {
+      s = 0.0;
+      h = GIMP_HSL_UNDEFINED;
+    }
+  else
+    {
+      if (l <= 0.5)
+        s = (max - min) / (max + min);
+      else
+        s = (max - min) / (2.0 - max - min);
+
+      delta = max - min;
+
+      if (delta == 0.0)
+        delta = 1.0;
+
+      if (red == max)
+        {
+          h = (green - blue) / delta;
+        }
+      else if (green == max)
+        {
+          h = 2.0 + (blue - red) / delta;
+        }
+      else if (blue == max)
+        {
+          h = 4.0 + (red - green) / delta;
+        }
+
+      h /= 6.0;
+
+      if (h < 0.0)
+        h += 1.0;
+    }
+
+  *hue        = h;
+  *saturation = s;
+  *luma       = l;
+}
+
+/**
  * gimp_hsv_to_rgb4:
  * @rgb:        RGB triplet, rgb[0] is red channel, rgb[1] is green,
  *              rgb[2] is blue (0..255)
@@ -1083,3 +1162,40 @@
   rgb[1] = ROUND (saturation * 255.0);
   rgb[2] = ROUND (value      * 255.0);
 }
+
+/**
+ * gimp_hsl_to_rgb4:
+ * @rgb:        RGB triplet, rgb[0] is red channel, rgb[1] is green,
+ *              rgb[2] is blue (0..255)
+ * @hue:        Hue channel (0..1)
+ * @saturation: Saturation channel (0..1)
+ * @luma:      Luma channel (0..1)
+ **/
+void
+gimp_hsl_to_rgb4 (guchar  *rgb,
+                  gdouble  hue,
+                  gdouble  saturation,
+                  gdouble  luma)
+{
+  if (saturation == 0.0)
+    {
+      hue        = luma;
+      saturation = luma;
+      luma       = luma;
+    }
+  else
+    {
+      gdouble m1, m2;
+
+      if (luma <= 0.5)
+        m2 = luma * (1.0 + saturation);
+      else
+        m2 = luma + saturation - luma * saturation;
+
+      m1 = 2.0 * luma - m2;
+
+      rgb[0] = ROUND (gimp_hsl_value (m1, m2, hue * 6.0 + 2.0) * 255.0);
+      rgb[1] = ROUND (gimp_hsl_value (m1, m2, hue * 6.0)       * 255.0);
+      rgb[2] = ROUND (gimp_hsl_value (m1, m2, hue * 6.0 - 2.0) * 255.0);
+    }
+}
--- ./libgimpcolor/gimpcolorspace.h~	2006-06-27 06:33:48.000000000 -0400
+++ ./libgimpcolor/gimpcolorspace.h	2007-01-06 13:07:47.000000000 -0500
@@ -94,6 +94,14 @@
                                  gdouble       hue,
                                  gdouble       saturation,
                                  gdouble       value);
+void    gimp_rgb_to_hsl4        (const guchar *rgb,
+                                 gdouble      *hue,
+                                 gdouble      *saturation,
+                                 gdouble      *luma);
+void    gimp_hsl_to_rgb4        (guchar       *rgb,
+                                 gdouble       hue,
+                                 gdouble       saturation,
+                                 gdouble       luma);
 
 
 G_END_DECLS
--- ./plug-ins/common/decompose.c~	2006-06-27 06:33:49.000000000 -0400
+++ ./plug-ins/common/decompose.c	2007-01-06 13:31:42.000000000 -0500
@@ -100,12 +100,20 @@
                               gint bpp, gint numpix, guchar **dst);
 static void extract_hsv      (const guchar *src,
                               gint bpp, gint numpix, guchar **dst);
+static void extract_hsl      (const guchar *src,
+                              gint bpp, gint numpix, guchar **dst);
 static void extract_hue      (const guchar *src,
                               gint bpp, gint numpix, guchar **dst);
 static void extract_sat      (const guchar *src,
                               gint bpp, gint numpix, guchar **dst);
 static void extract_val      (const guchar *src,
                               gint bpp, gint numpix, guchar **dst);
+static void extract_huel     (const guchar *src,
+                              gint bpp, gint numpix, guchar **dst);
+static void extract_satl     (const guchar *src,
+                              gint bpp, gint numpix, guchar **dst);
+static void extract_luma     (const guchar *src,
+                              gint bpp, gint numpix, guchar **dst);
 static void extract_cmy      (const guchar *src,
                               gint bpp, gint numpix, guchar **dst);
 static void extract_cyan     (const guchar *src,
@@ -182,6 +190,15 @@
   { N_("Value"),      FALSE, 1, { N_("value")    }, extract_val },
 
 
+  { N_("HSL"),        TRUE,  3, { N_("hue_l"),
+                                  N_("saturation_l"),
+                                  N_("luma")    }, extract_hsl },
+
+  { N_("Hue (L)"),    FALSE, 1, { N_("hue_l")   }, extract_huel },
+  { N_("Saturation (L)"), FALSE, 1, { N_("saturation_l") }, extract_satl },
+  { N_("Luma"),       FALSE, 1, { N_("luma")    }, extract_luma },
+
+
   { N_("CMY"),        TRUE,  3, { N_("cyan"),
                                   N_("magenta"),
                                   N_("yellow")  }, extract_cmy },
@@ -957,6 +974,90 @@
 
 
 static void
+extract_hsl (const guchar  *src,
+	     gint           bpp,
+	     gint           numpix,
+	     guchar       **dst)
+{
+  register const guchar *rgb_src = src;
+  register guchar *hue_dst = dst[0];
+  register guchar *sat_dst = dst[1];
+  register guchar *lum_dst = dst[2];
+  register gint count = numpix, offset = bpp;
+  gdouble hue, sat, val;
+
+  while (count-- > 0)
+    {
+      gimp_rgb_to_hsl4 (rgb_src, &hue, &sat, &val);
+      *hue_dst++ = (guchar) (hue * 255.999);
+      *sat_dst++ = (guchar) (sat * 255.999);
+      *lum_dst++ = (guchar) (val * 255.999);
+      rgb_src += offset;
+    }
+}
+
+
+static void
+extract_huel (const guchar  *src,
+	     gint           bpp,
+	     gint           numpix,
+	     guchar       **dst)
+{
+  register const guchar *rgb_src = src;
+  register guchar *hue_dst = dst[0];
+  register gint count = numpix, offset = bpp;
+  gdouble hue, dummy;
+
+  while (count-- > 0)
+    {
+      gimp_rgb_to_hsl4 (rgb_src, &hue, &dummy, &dummy);
+      *hue_dst++ = (guchar) (hue * 255.999);
+      rgb_src += offset;
+    }
+}
+
+
+static void
+extract_satl (const guchar  *src,
+	     gint           bpp,
+	     gint           numpix,
+	     guchar       **dst)
+{
+  register const guchar *rgb_src = src;
+  register guchar *sat_dst = dst[0];
+  register gint count = numpix, offset = bpp;
+  gdouble sat, dummy;
+
+  while (count-- > 0)
+    {
+      gimp_rgb_to_hsl4 (rgb_src, &dummy, &sat, &dummy);
+      *sat_dst++ = (guchar) (sat * 255.999);
+      rgb_src += offset;
+    }
+}
+
+
+static void
+extract_luma (const guchar  *src,
+	      gint           bpp,
+	      gint           numpix,
+	      guchar       **dst)
+{
+  register const guchar *rgb_src = src;
+  register guchar *lum_dst = dst[0];
+  register gint count = numpix, offset = bpp;
+  gdouble lum, dummy;
+
+  while (count-- > 0)
+    {
+      gimp_rgb_to_hsl4 (rgb_src, &dummy, &dummy, &lum);
+      *lum_dst++ = (guchar) (lum * 255.999);
+      rgb_src += offset;
+    }
+}
+
+
+static void
 extract_cyan (const guchar  *src,
 	      gint           bpp,
 	      gint           numpix,
--- ./plug-ins/common/compose.c~	2006-09-01 07:14:34.000000000 -0400
+++ ./plug-ins/common/compose.c	2007-01-06 13:08:52.000000000 -0500
@@ -93,6 +93,11 @@
                                       gint             numpix,
                                       guchar          *dst,
                                       gboolean         dst_has_alpha);
+static void      compose_hsl         (guchar         **src,
+                                      gint            *incr,
+                                      gint             numpix,
+                                      guchar          *dst,
+                                      gboolean         dst_has_alpha);
 static void      compose_cmy         (guchar         **src,
                                       gint            *incr,
                                       gint             numpix,
@@ -211,6 +216,14 @@
     { NULL, NULL, NULL, NULL },
     "hsv-compose",  compose_hsv },
 
+  { N_("HSL"), 3,
+    { N_("_Hue:"),
+      N_("_Saturation:"),
+      N_("_Luma:"),
+      NULL },
+    { NULL, NULL, NULL, NULL },
+    "hsl-compose",  compose_hsl },
+
   { N_("CMY"), 3,
     { N_("_Cyan:"),
       N_("_Magenta:"),
@@ -337,7 +350,7 @@
     { GIMP_PDB_IMAGE,    "image2",       "Second input image" },
     { GIMP_PDB_IMAGE,    "image3",       "Third input image" },
     { GIMP_PDB_IMAGE,    "image4",       "Fourth input image" },
-    { GIMP_PDB_STRING,   "compose-type", "What to compose: RGB, RGBA, HSV, CMY, CMYK" },
+    { GIMP_PDB_STRING,   "compose-type", "What to compose: RGB, RGBA, HSV, HSL, CMY, CMYK" },
     { GIMP_PDB_INT8,     "value1",       "Mask value if image 1 is -1" },
     { GIMP_PDB_INT8,     "value2",       "Mask value if image 2 is -1" },
     { GIMP_PDB_INT8,     "value3",       "Mask value if image 3 is -1" },
@@ -357,7 +370,7 @@
     { GIMP_PDB_DRAWABLE, "drawable2",    "Second input drawable" },
     { GIMP_PDB_DRAWABLE, "drawable3",    "Third input drawable" },
     { GIMP_PDB_DRAWABLE, "drawable4",    "Fourth input drawable" },
-    { GIMP_PDB_STRING,   "compose-type", "What to compose: RGB, RGBA, HSV, CMY, CMYK" },
+    { GIMP_PDB_STRING,   "compose-type", "What to compose: RGB, RGBA, HSV, HSL, CMY, CMYK" },
     { GIMP_PDB_INT8,     "value1",       "Mask value if image 1 is -1" },
     { GIMP_PDB_INT8,     "value2",       "Mask value if image 2 is -1" },
     { GIMP_PDB_INT8,     "value3",       "Mask value if image 3 is -1" },
@@ -1035,6 +1048,38 @@
 
 
 static void
+compose_hsl (guchar **src,
+             gint    *incr_src,
+             gint     numpix,
+             guchar  *dst,
+             gboolean dst_has_alpha)
+{
+  register const guchar *hue_src = src[0];
+  register const guchar *sat_src = src[1];
+  register const guchar *lum_src = src[2];
+  register       guchar *rgb_dst = dst;
+  register       gint    count   = numpix;
+  gint hue_incr = incr_src[0];
+  gint sat_incr = incr_src[1];
+  gint lum_incr = incr_src[2];
+
+  while (count-- > 0)
+    {
+      gimp_hsl_to_rgb4 (rgb_dst, (gdouble) *hue_src / 255.0,
+			         (gdouble) *sat_src / 255.0,
+			         (gdouble) *lum_src / 255.0);
+      rgb_dst += 3;
+      hue_src += hue_incr;
+      sat_src += sat_incr;
+      lum_src += lum_incr;
+
+      if (dst_has_alpha)
+        rgb_dst++;
+    }
+}
+
+
+static void
 compose_cmy (guchar **src,
              gint    *incr_src,
              gint     numpix,
_______________________________________________
Gimp-developer mailing list
Gimp-developer@xxxxxxxxxxxxxxxxxxxxxx
https://lists.XCF.Berkeley.EDU/mailman/listinfo/gimp-developer

[Index of Archives]     [Video For Linux]     [Photo]     [Yosemite News]     [gtk]     [GIMP for Windows]     [KDE]     [GEGL]     [Gimp's Home]     [Gimp on GUI]     [Gimp on Windows]     [Steve's Art]

  Powered by Linux