RSS/Atom feed Twitter
Site is read-only, email is disabled

PATCH: Add HSL to compose/decompose plugins

This discussion is connected to the gimp-developer-list.gnome.org mailing list which is provided by the GIMP developers and not related to gimpusers.com.

This is a read-only list on gimpusers.com so this discussion thread is read-only, too.

5 of 5 messages available
Toggle history

Please log in to manage your subscriptions.

PATCH: Add HSL to compose/decompose plugins Robert L Krawitz 06 Jan 20:05
  PATCH: Add HSL to compose/decompose plugins Sven Neumann 06 Jan 20:11
   PATCH: Add HSL to compose/decompose plugins Robert L Krawitz 06 Jan 20:41
   PATCH: Add HSL to compose/decompose plugins Robert L Krawitz 06 Jan 20:43
   PATCH: Add HSL to compose/decompose plugins Robert L Krawitz 06 Jan 20:58
Robert L Krawitz
2007-01-06 20:05:13 UTC (almost 18 years ago)

PATCH: Add HSL to compose/decompose plugins

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.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)
+ {
+ 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,

Sven Neumann
2007-01-06 20:11:06 UTC (almost 18 years ago)

PATCH: Add HSL to compose/decompose plugins

Hi,

the new function in libgimpcolor needs to be marked as new in 2.4. You can do that by adding "Since: GIMP 2.4" as the last line in the gtk-doc comment. The gtk-doc comment also lacks a description of what the function does. If that is corrected, I think we can still include this patch for 2.4.

Sven

Robert L Krawitz
2007-01-06 20:41:27 UTC (almost 18 years ago)

PATCH: Add HSL to compose/decompose plugins

From: Sven Neumann
Date: Sat, 06 Jan 2007 20:11:06 +0100

the new function in libgimpcolor needs to be marked as new in 2.4. You can do that by adding "Since: GIMP 2.4" as the last line in the gtk-doc comment. The gtk-doc comment also lacks a description of what the function does. If that is corrected, I think we can still include this patch for 2.4.

Where does this go? Does it go in here somewhere?

/** * 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)
**/

Robert L Krawitz
2007-01-06 20:43:00 UTC (almost 18 years ago)

PATCH: Add HSL to compose/decompose plugins

BTW, I believe I also have to add appropriate lines to gimpcolor.def, correct?

Robert L Krawitz
2007-01-06 20:58:57 UTC (almost 18 years ago)

PATCH: Add HSL to compose/decompose plugins

From: Sven Neumann
Cc: gimp-developer@lists.xcf.berkeley.edu Date: Sat, 06 Jan 2007 20:11:06 +0100

Hi,

the new function in libgimpcolor needs to be marked as new in 2.4. You can do that by adding "Since: GIMP 2.4" as the last line in the gtk-doc comment. The gtk-doc comment also lacks a description of what the function does. If that is corrected, I think we can still include this patch for 2.4.

Never mind, I figured it out. --- ./libgimpcolor/gimpcolorspace.c~ 2006-06-27 06:33:48.000000000 -0400 +++ ./libgimpcolor/gimpcolorspace.c 2007-01-06 14:57:28.000000000 -0500 @@ -1003,6 +1003,90 @@
}

/**
+ * 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) + *
+ * Convert an RGB color value to an HSL (Hue, Saturation, Lightness) + * color value.
+ *
+ * Since: GIMP 2.4
+ **/
+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.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 +1167,45 @@
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) + *
+ * Convert an HSL color value (Hue, Saturation, Lightness) to an RGB + * color value.
+ *
+ * Since: GIMP 2.4
+ **/
+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)
+ {
+ 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,