[gimp] Bug 749902 - Add Hue-Chroma operation/tool and LCH color selector



commit 2b167d63372e79a990383f7f9f52ccb8ca730e13
Author: Michael Natterer <mitch gimp org>
Date:   Wed May 17 19:28:40 2017 +0200

    Bug 749902 - Add Hue-Chroma operation/tool and LCH color selector
    
    Add LCH to the color selectors, patch by Elle Stone and myself.
    
    - Extend enum GimpColorSelectorChannel by LCH channels
    - Support them in GimpColorScale, GimpColorScales and GimpColorSelect
    - Did not yet remove the HSV channels until things are working 100% ok
    - Change drawing in GimpColorSelect to be much faster, to compensate
      for babl_process() making the LCH modes pretty slow
    - Clean up stuff in GimpColorSelect
    
    This is WIP and should not be considered finished, biggest TODO is
    displaying out-of-gamut values in GimpColorScales' spinbuttons.

 libgimpwidgets/gimpcolorscale.c   |  138 ++++++--
 libgimpwidgets/gimpcolorscales.c  |  140 +++++---
 libgimpwidgets/gimpcolorselect.c  |  743 ++++++++++++++++++++++++++++++++-----
 libgimpwidgets/gimpwidgetsenums.c |   12 +-
 libgimpwidgets/gimpwidgetsenums.h |   34 +-
 5 files changed, 898 insertions(+), 169 deletions(-)
---
diff --git a/libgimpwidgets/gimpcolorscale.c b/libgimpwidgets/gimpcolorscale.c
index 4064659..5c6d429 100644
--- a/libgimpwidgets/gimpcolorscale.c
+++ b/libgimpwidgets/gimpcolorscale.c
@@ -54,12 +54,21 @@ enum
 };
 
 
+typedef struct _GimpLCH  GimpLCH;
+
+struct _GimpLCH
+{
+  gdouble l, c, h, a;
+};
+
+
 typedef struct _GimpColorScalePrivate GimpColorScalePrivate;
 
 struct _GimpColorScalePrivate
 {
   GimpColorConfig    *config;
   GimpColorTransform *transform;
+  guchar              oog_color[3];
 };
 
 #define GET_PRIVATE(obj) \
@@ -98,12 +107,18 @@ static void     gimp_color_scale_render_stipple    (GimpColorScale   *scale);
 
 static void     gimp_color_scale_create_transform  (GimpColorScale   *scale);
 static void     gimp_color_scale_destroy_transform (GimpColorScale   *scale);
+static void     gimp_color_scale_notify_config     (GimpColorConfig  *config,
+                                                    const GParamSpec *pspec,
+                                                    GimpColorScale   *scale);
 
 
 G_DEFINE_TYPE (GimpColorScale, gimp_color_scale, GTK_TYPE_SCALE)
 
 #define parent_class gimp_color_scale_parent_class
 
+static const Babl *fish_rgb_to_lch = NULL;
+static const Babl *fish_lch_to_rgb = NULL;
+
 
 static void
 gimp_color_scale_class_init (GimpColorScaleClass *klass)
@@ -138,6 +153,18 @@ gimp_color_scale_class_init (GimpColorScaleClass *klass)
                                                       G_PARAM_CONSTRUCT));
 
   g_type_class_add_private (object_class, sizeof (GimpColorScalePrivate));
+
+  /*  This is so ugly... we have to babl_init() here so the binary
+   *  generated by gtk-doc to scan libgimpwidgets' types won't crash.
+   *  I didn't find a way to inject this line of code into the
+   *  generated source.
+   */
+  babl_init ();
+
+  fish_rgb_to_lch = babl_fish (babl_format ("R'G'B'A double"),
+                               babl_format ("CIE LCH(ab) double"));
+  fish_lch_to_rgb = babl_fish (babl_format ("CIE LCH(ab) double"),
+                               babl_format ("R'G'B' double"));
 }
 
 static void
@@ -706,7 +733,7 @@ gimp_color_scale_set_color_config (GimpColorScale  *scale,
       if (priv->config)
         {
           g_signal_handlers_disconnect_by_func (priv->config,
-                                                gimp_color_scale_destroy_transform,
+                                                gimp_color_scale_notify_config,
                                                 scale);
           g_object_unref (priv->config);
 
@@ -719,9 +746,11 @@ gimp_color_scale_set_color_config (GimpColorScale  *scale,
         {
           g_object_ref (priv->config);
 
-          g_signal_connect_swapped (priv->config, "notify",
-                                    G_CALLBACK (gimp_color_scale_destroy_transform),
-                                    scale);
+          g_signal_connect (priv->config, "notify",
+                            G_CALLBACK (gimp_color_scale_notify_config),
+                            scale);
+
+          gimp_color_scale_notify_config (priv->config, NULL, scale);
         }
     }
 }
@@ -753,15 +782,19 @@ should_invert (GtkRange *range)
 static void
 gimp_color_scale_render (GimpColorScale *scale)
 {
-  GtkRange *range = GTK_RANGE (scale);
-  GimpRGB   rgb;
-  GimpHSV   hsv;
-  guint     x, y;
-  gdouble  *channel_value = NULL; /* shut up compiler */
-  gboolean  to_rgb        = FALSE;
-  gboolean  invert;
-  guchar   *buf;
-  guchar   *d;
+  GimpColorScalePrivate *priv  = GET_PRIVATE (scale);
+  GtkRange              *range = GTK_RANGE (scale);
+  GimpRGB                rgb;
+  GimpHSV                hsv;
+  GimpLCH                lch;
+  gint                   multiplier = 1;
+  guint                  x, y;
+  gdouble               *channel_value = NULL; /* shut up compiler */
+  gboolean               from_hsv      = FALSE;
+  gboolean               from_lch      = FALSE;
+  gboolean               invert;
+  guchar                *buf;
+  guchar                *d;
 
   if ((buf = scale->buf) == NULL)
     return;
@@ -774,16 +807,22 @@ gimp_color_scale_render (GimpColorScale *scale)
 
   rgb = scale->rgb;
   hsv = scale->hsv;
+  babl_process (fish_rgb_to_lch, &rgb, &lch, 1);
 
   switch (scale->channel)
     {
     case GIMP_COLOR_SELECTOR_HUE:        channel_value = &hsv.h; break;
     case GIMP_COLOR_SELECTOR_SATURATION: channel_value = &hsv.s; break;
     case GIMP_COLOR_SELECTOR_VALUE:      channel_value = &hsv.v; break;
+
     case GIMP_COLOR_SELECTOR_RED:        channel_value = &rgb.r; break;
     case GIMP_COLOR_SELECTOR_GREEN:      channel_value = &rgb.g; break;
     case GIMP_COLOR_SELECTOR_BLUE:       channel_value = &rgb.b; break;
     case GIMP_COLOR_SELECTOR_ALPHA:      channel_value = &rgb.a; break;
+
+    case GIMP_COLOR_SELECTOR_LCH_LIGHTNESS: channel_value = &lch.l; break;
+    case GIMP_COLOR_SELECTOR_LCH_CHROMA:    channel_value = &lch.c; break;
+    case GIMP_COLOR_SELECTOR_LCH_HUE:       channel_value = &lch.h; break;
     }
 
   switch (scale->channel)
@@ -791,7 +830,20 @@ gimp_color_scale_render (GimpColorScale *scale)
     case GIMP_COLOR_SELECTOR_HUE:
     case GIMP_COLOR_SELECTOR_SATURATION:
     case GIMP_COLOR_SELECTOR_VALUE:
-      to_rgb = TRUE;
+      from_hsv = TRUE;
+      break;
+
+    case GIMP_COLOR_SELECTOR_LCH_LIGHTNESS:
+      multiplier = 100;
+      from_lch = TRUE;
+      break;
+    case GIMP_COLOR_SELECTOR_LCH_CHROMA:
+      multiplier = 100;
+      from_lch = TRUE;
+      break;
+    case GIMP_COLOR_SELECTOR_LCH_HUE:
+      multiplier = 360;
+      from_lch = TRUE;
       break;
 
     default:
@@ -805,18 +857,31 @@ gimp_color_scale_render (GimpColorScale *scale)
     case GTK_ORIENTATION_HORIZONTAL:
       for (x = 0, d = buf; x < scale->width; x++, d += 4)
         {
-          gdouble value = (gdouble) x / (gdouble) (scale->width - 1);
+          gdouble value = (gdouble) x * multiplier / (gdouble) (scale->width - 1);
           guchar  r, g, b;
 
           if (invert)
-            value = 1.0 - value;
+            value = multiplier - value;
 
           *channel_value = value;
 
-          if (to_rgb)
+          if (from_hsv)
             gimp_hsv_to_rgb (&hsv, &rgb);
+          else if (from_lch)
+            babl_process (fish_lch_to_rgb, &lch, &rgb, 1);
 
-          gimp_rgb_get_uchar (&rgb, &r, &g, &b);
+          if (rgb.r < 0.0 || rgb.r > 1.0 ||
+              rgb.g < 0.0 || rgb.g > 1.0 ||
+              rgb.b < 0.0 || rgb.b > 1.0)
+            {
+              r = priv->oog_color[0];
+              g = priv->oog_color[1];
+              b = priv->oog_color[2];
+            }
+          else
+            {
+              gimp_rgb_get_uchar (&rgb, &r, &g, &b);
+            }
 
           GIMP_CAIRO_RGB24_SET_PIXEL (d, r, g, b);
         }
@@ -832,18 +897,31 @@ gimp_color_scale_render (GimpColorScale *scale)
     case GTK_ORIENTATION_VERTICAL:
       for (y = 0; y < scale->height; y++)
         {
-          gdouble value = (gdouble) y / (gdouble) (scale->height - 1);
+          gdouble value = (gdouble) y * multiplier / (gdouble) (scale->height - 1);
           guchar  r, g, b;
 
           if (invert)
-            value = 1.0 - value;
+            value = multiplier - value;
 
           *channel_value = value;
 
-          if (to_rgb)
+          if (from_hsv)
             gimp_hsv_to_rgb (&hsv, &rgb);
+          else if (from_lch)
+            babl_process (fish_lch_to_rgb, &lch, &rgb, 1);
 
-          gimp_rgb_get_uchar (&rgb, &r, &g, &b);
+          if (rgb.r < 0.0 || rgb.r > 1.0 ||
+              rgb.g < 0.0 || rgb.g > 1.0 ||
+              rgb.b < 0.0 || rgb.b > 1.0)
+            {
+              r = priv->oog_color[0];
+              g = priv->oog_color[1];
+              b = priv->oog_color[2];
+            }
+          else
+            {
+              gimp_rgb_get_uchar (&rgb, &r, &g, &b);
+            }
 
           for (x = 0, d = buf; x < scale->width; x++, d += 4)
             {
@@ -1053,3 +1131,19 @@ gimp_color_scale_destroy_transform (GimpColorScale *scale)
 
   gtk_widget_queue_draw (GTK_WIDGET (scale));
 }
+
+static void
+gimp_color_scale_notify_config (GimpColorConfig  *config,
+                                const GParamSpec *pspec,
+                                GimpColorScale   *scale)
+{
+  GimpColorScalePrivate *priv = GET_PRIVATE (scale);
+
+  gimp_color_scale_destroy_transform (scale);
+
+  gimp_rgb_get_uchar (&config->out_of_gamut_color,
+                      priv->oog_color,
+                      priv->oog_color + 1,
+                      priv->oog_color + 2);
+  scale->needs_render = TRUE;
+}
diff --git a/libgimpwidgets/gimpcolorscales.c b/libgimpwidgets/gimpcolorscales.c
index c583f3c..6b8e489 100644
--- a/libgimpwidgets/gimpcolorscales.c
+++ b/libgimpwidgets/gimpcolorscales.c
@@ -49,10 +49,18 @@
  *
  * The #GimpColorScales widget is an implementation of a
  * #GimpColorSelector. It shows a group of #GimpColorScale widgets
- * that allow to adjust the HSV and RGB color channels.
+ * that allow to adjust the HSV, LCH, and RGB color channels.
  **/
 
 
+typedef struct _GimpLCH  GimpLCH;
+
+struct _GimpLCH
+{
+  gdouble l, c, h, a;
+};
+
+
 #define GIMP_COLOR_SCALES_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_COLOR_SCALES, 
GimpColorScalesClass))
 #define GIMP_IS_COLOR_SCALES_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_COLOR_SCALES))
 #define GIMP_COLOR_SCALES_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_COLOR_SCALES, 
GimpColorScalesClass))
@@ -64,9 +72,9 @@ struct _GimpColorScales
 {
   GimpColorSelector  parent_instance;
 
-  GtkWidget         *toggles[7];
-  GtkWidget         *sliders[7];
-  GtkObject         *slider_data[7];
+  GtkWidget         *toggles[10];
+  GtkWidget         *sliders[10];
+  GtkObject         *slider_data[10];
 };
 
 struct _GimpColorScalesClass
@@ -92,9 +100,9 @@ static void   gimp_color_scales_set_config     (GimpColorSelector *selector,
 
 static void   gimp_color_scales_update_scales  (GimpColorScales   *scales,
                                                 gint               skip);
-static void   gimp_color_scales_toggle_update  (GtkWidget         *widget,
+static void   gimp_color_scales_toggle_changed (GtkWidget         *widget,
                                                 GimpColorScales   *scales);
-static void   gimp_color_scales_scale_update   (GtkAdjustment     *adjustment,
+static void   gimp_color_scales_scale_changed  (GtkAdjustment     *adjustment,
                                                 GimpColorScales   *scales);
 
 
@@ -102,6 +110,9 @@ G_DEFINE_TYPE (GimpColorScales, gimp_color_scales, GIMP_TYPE_COLOR_SELECTOR)
 
 #define parent_class gimp_color_scales_parent_class
 
+static const Babl *fish_rgb_to_lch = NULL;
+static const Babl *fish_lch_to_rgb = NULL;
+
 
 static void
 gimp_color_scales_class_init (GimpColorScalesClass *klass)
@@ -117,6 +128,11 @@ gimp_color_scales_class_init (GimpColorScalesClass *klass)
   selector_class->set_color             = gimp_color_scales_set_color;
   selector_class->set_channel           = gimp_color_scales_set_channel;
   selector_class->set_config            = gimp_color_scales_set_config;
+
+  fish_rgb_to_lch = babl_fish (babl_format ("R'G'B'A double"),
+                               babl_format ("CIE LCH(ab) double"));
+  fish_lch_to_rgb = babl_fish (babl_format ("CIE LCH(ab) double"),
+                               babl_format ("R'G'B' double"));
 }
 
 static void
@@ -129,19 +145,21 @@ gimp_color_scales_init (GimpColorScales *scales)
   gint               i;
 
   static const gdouble slider_initial_vals[] =
-    {   0,   0,   0,   0,   0,   0,   0 };
+  /*{   H,   S,   V,   R,   G,   B,   A,   L,   C,   H }*/
+    {   0,   0,   0,   0,   0,   0,   0,   0,   0,   0 };
   static const gdouble slider_max_vals[] =
-    { 360, 100, 100, 100, 100, 100, 100 };
+    { 360, 100, 100, 100, 100, 100, 100, 100, 100, 360 };
   static const gdouble slider_incs[] =
-    {  30,  10,  10,  16,  16,  16,  10 };
+    {  30,  10,  10,  16,  16,  16,  10,  10,  10,  30 };
 
   /*  don't needs the toggles for our own operation  */
   selector->toggles_visible = FALSE;
 
-  table = gtk_table_new (7, 4, FALSE);
+  table = gtk_table_new (11, 4, FALSE);
   gtk_table_set_row_spacings (GTK_TABLE (table), 1);
-  gtk_table_set_row_spacing (GTK_TABLE (table), 2, 5); /* hsv <-> rgb   */
-  gtk_table_set_row_spacing (GTK_TABLE (table), 5, 5); /* rgb <-> alpha */
+  gtk_table_set_row_spacing (GTK_TABLE (table), 2, 5); /* hsv   <-> rgb   */
+  gtk_table_set_row_spacing (GTK_TABLE (table), 5, 5); /* rgb   <-> alpha */
+  gtk_table_set_row_spacing (GTK_TABLE (table), 6, 5); /* alpha <-> lch   */
   gtk_table_set_col_spacings (GTK_TABLE (table), 2);
   gtk_table_set_col_spacing (GTK_TABLE (table), 0, 0);
   gtk_box_pack_start (GTK_BOX (scales), table, FALSE, FALSE, 0);
@@ -151,11 +169,9 @@ gimp_color_scales_init (GimpColorScales *scales)
 
   group = NULL;
 
-  for (i = GIMP_COLOR_SELECTOR_HUE; i <= GIMP_COLOR_SELECTOR_ALPHA; i++)
+  for (i = 0; i < 10; i++)
     {
-      GimpEnumDesc *enum_desc;
-
-      enum_desc = gimp_enum_get_desc (enum_class, i);
+      GimpEnumDesc *enum_desc = gimp_enum_get_desc (enum_class, i);
 
       if (i == GIMP_COLOR_SELECTOR_ALPHA)
         {
@@ -176,7 +192,7 @@ gimp_color_scales_init (GimpColorScales *scales)
                                    gettext (enum_desc->value_help), NULL);
 
           g_signal_connect (scales->toggles[i], "toggled",
-                            G_CALLBACK (gimp_color_scales_toggle_update),
+                            G_CALLBACK (gimp_color_scales_toggle_changed),
                             scales);
         }
 
@@ -198,7 +214,7 @@ gimp_color_scales_init (GimpColorScales *scales)
       gimp_color_scale_set_channel (GIMP_COLOR_SCALE (scales->sliders[i]), i);
 
       g_signal_connect (scales->slider_data[i], "value-changed",
-                        G_CALLBACK (gimp_color_scales_scale_update),
+                        G_CALLBACK (gimp_color_scales_scale_changed),
                         scales);
     }
 
@@ -212,8 +228,9 @@ gimp_color_scales_togg_sensitive (GimpColorSelector *selector,
   GimpColorScales *scales = GIMP_COLOR_SCALES (selector);
   gint             i;
 
-  for (i = 0; i < 6; i++)
-    gtk_widget_set_sensitive (scales->toggles[i], sensitive);
+  for (i = 0; i < 10; i++)
+    if (scales->toggles[i])
+      gtk_widget_set_sensitive (scales->toggles[i], sensitive);
 }
 
 static void
@@ -223,8 +240,9 @@ gimp_color_scales_togg_visible (GimpColorSelector *selector,
   GimpColorScales *scales = GIMP_COLOR_SCALES (selector);
   gint             i;
 
-  for (i = 0; i < 6; i++)
-    gtk_widget_set_visible (scales->toggles[i], visible);
+  for (i = 0; i < 10; i++)
+    if (scales->toggles[i])
+      gtk_widget_set_visible (scales->toggles[i], visible);
 }
 
 static void
@@ -269,17 +287,17 @@ gimp_color_scales_set_channel (GimpColorSelector        *selector,
 {
   GimpColorScales *scales = GIMP_COLOR_SCALES (selector);
 
-  if (channel < 7)
+  if (scales->toggles[channel])
     {
       g_signal_handlers_block_by_func (scales->toggles[channel],
-                                       gimp_color_scales_toggle_update,
+                                       gimp_color_scales_toggle_changed,
                                        scales);
 
       gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (scales->toggles[channel]),
                                     TRUE);
 
       g_signal_handlers_unblock_by_func (scales->toggles[channel],
-                                         gimp_color_scales_toggle_update,
+                                         gimp_color_scales_toggle_changed,
                                          scales);
     }
 }
@@ -291,7 +309,7 @@ gimp_color_scales_set_config (GimpColorSelector *selector,
   GimpColorScales *scales = GIMP_COLOR_SCALES (selector);
   gint             i;
 
-  for (i = 0; i < 7; i++)
+  for (i = 0; i < 10; i++)
     {
       if (scales->sliders[i])
         gimp_color_scale_set_color_config (GIMP_COLOR_SCALE (scales->sliders[i]),
@@ -304,30 +322,38 @@ gimp_color_scales_update_scales (GimpColorScales *scales,
                                  gint             skip)
 {
   GimpColorSelector *selector = GIMP_COLOR_SELECTOR (scales);
-  gint               values[7];
+  GimpLCH            lch;
+  gdouble            values[10];
   gint               i;
 
-  values[GIMP_COLOR_SELECTOR_HUE]        = ROUND (selector->hsv.h * 360.0);
-  values[GIMP_COLOR_SELECTOR_SATURATION] = ROUND (selector->hsv.s * 100.0);
-  values[GIMP_COLOR_SELECTOR_VALUE]      = ROUND (selector->hsv.v * 100.0);
-  values[GIMP_COLOR_SELECTOR_RED]        = ROUND (selector->rgb.r * 100.0);
-  values[GIMP_COLOR_SELECTOR_GREEN]      = ROUND (selector->rgb.g * 100.0);
-  values[GIMP_COLOR_SELECTOR_BLUE]       = ROUND (selector->rgb.b * 100.0);
-  values[GIMP_COLOR_SELECTOR_ALPHA]      = ROUND (selector->rgb.a * 100.0);
+  babl_process (fish_rgb_to_lch, &selector->rgb, &lch, 1);
+
+  values[GIMP_COLOR_SELECTOR_HUE]           = selector->hsv.h * 360.0;
+  values[GIMP_COLOR_SELECTOR_SATURATION]    = selector->hsv.s * 100.0;
+  values[GIMP_COLOR_SELECTOR_VALUE]         = selector->hsv.v * 100.0;
+
+  values[GIMP_COLOR_SELECTOR_RED]           = selector->rgb.r * 100.0;
+  values[GIMP_COLOR_SELECTOR_GREEN]         = selector->rgb.g * 100.0;
+  values[GIMP_COLOR_SELECTOR_BLUE]          = selector->rgb.b * 100.0;
+  values[GIMP_COLOR_SELECTOR_ALPHA]         = selector->rgb.a * 100.0;
+
+  values[GIMP_COLOR_SELECTOR_LCH_LIGHTNESS] = lch.l;
+  values[GIMP_COLOR_SELECTOR_LCH_CHROMA]    = lch.c;
+  values[GIMP_COLOR_SELECTOR_LCH_HUE]       = lch.h;
 
-  for (i = 0; i < 7; i++)
+  for (i = 0; i < 10; i++)
     {
       if (i != skip)
         {
           g_signal_handlers_block_by_func (scales->slider_data[i],
-                                           gimp_color_scales_scale_update,
+                                           gimp_color_scales_scale_changed,
                                            scales);
 
           gtk_adjustment_set_value (GTK_ADJUSTMENT (scales->slider_data[i]),
                                     values[i]);
 
           g_signal_handlers_unblock_by_func (scales->slider_data[i],
-                                             gimp_color_scales_scale_update,
+                                             gimp_color_scales_scale_changed,
                                              scales);
         }
 
@@ -337,8 +363,8 @@ gimp_color_scales_update_scales (GimpColorScales *scales,
 }
 
 static void
-gimp_color_scales_toggle_update (GtkWidget       *widget,
-                                 GimpColorScales *scales)
+gimp_color_scales_toggle_changed (GtkWidget       *widget,
+                                  GimpColorScales *scales)
 {
   GimpColorSelector *selector = GIMP_COLOR_SELECTOR (scales);
 
@@ -346,7 +372,7 @@ gimp_color_scales_toggle_update (GtkWidget       *widget,
     {
       gint i;
 
-      for (i = 0; i < 6; i++)
+      for (i = 0; i < 10; i++)
         if (widget == scales->toggles[i])
           {
             selector->channel = (GimpColorSelectorChannel) i;
@@ -358,14 +384,15 @@ gimp_color_scales_toggle_update (GtkWidget       *widget,
 }
 
 static void
-gimp_color_scales_scale_update (GtkAdjustment   *adjustment,
-                                GimpColorScales *scales)
+gimp_color_scales_scale_changed (GtkAdjustment   *adjustment,
+                                 GimpColorScales *scales)
 {
   GimpColorSelector *selector = GIMP_COLOR_SELECTOR (scales);
   gdouble            value    = gtk_adjustment_get_value (adjustment);
+  GimpLCH            lch;
   gint               i;
 
-  for (i = 0; i < 7; i++)
+  for (i = 0; i < 10; i++)
     if (scales->slider_data[i] == GTK_OBJECT (adjustment))
       break;
 
@@ -398,13 +425,36 @@ gimp_color_scales_scale_update (GtkAdjustment   *adjustment,
     case GIMP_COLOR_SELECTOR_ALPHA:
       selector->hsv.a = selector->rgb.a = value / 100.0;
       break;
+
+    case GIMP_COLOR_SELECTOR_LCH_LIGHTNESS:
+      babl_process (fish_rgb_to_lch, &selector->rgb, &lch, 1);
+      lch.l = value;
+      break;
+
+    case GIMP_COLOR_SELECTOR_LCH_CHROMA:
+      babl_process (fish_rgb_to_lch, &selector->rgb, &lch, 1);
+      lch.c = value;
+      break;
+
+    case GIMP_COLOR_SELECTOR_LCH_HUE:
+      babl_process (fish_rgb_to_lch, &selector->rgb, &lch, 1);
+      lch.h = value;
+      break;
     }
 
-  if ((i >= GIMP_COLOR_SELECTOR_HUE) && (i <= GIMP_COLOR_SELECTOR_VALUE))
+  if ((i >= GIMP_COLOR_SELECTOR_HUE) &&
+      (i <= GIMP_COLOR_SELECTOR_VALUE))
     {
       gimp_hsv_to_rgb (&selector->hsv, &selector->rgb);
     }
-  else if ((i >= GIMP_COLOR_SELECTOR_RED) && (i <= GIMP_COLOR_SELECTOR_BLUE))
+  else if ((i >= GIMP_COLOR_SELECTOR_LCH_LIGHTNESS) &&
+           (i <= GIMP_COLOR_SELECTOR_LCH_HUE))
+    {
+      babl_process (fish_lch_to_rgb, &lch, &selector->rgb, 1);
+      gimp_rgb_to_hsv (&selector->rgb, &selector->hsv);
+    }
+  else if ((i >= GIMP_COLOR_SELECTOR_RED) &&
+           (i <= GIMP_COLOR_SELECTOR_BLUE))
     {
       gimp_rgb_to_hsv (&selector->rgb, &selector->hsv);
     }
diff --git a/libgimpwidgets/gimpcolorselect.c b/libgimpwidgets/gimpcolorselect.c
index cbf1d92..e449c63 100644
--- a/libgimpwidgets/gimpcolorselect.c
+++ b/libgimpwidgets/gimpcolorselect.c
@@ -28,6 +28,7 @@
 #include <gtk/gtk.h>
 
 #include "libgimpbase/gimpbase.h"
+#include "libgimpconfig/gimpconfig.h"
 #include "libgimpcolor/gimpcolor.h"
 #include "libgimpmath/gimpmath.h"
 
@@ -37,7 +38,7 @@
 #include "gimpcolorselect.h"
 #include "gimphelpui.h"
 #include "gimpicons.h"
-#include "gimppreviewarea.h"
+#include "gimpwidgetsutils.h"
 #include "gimp3migration.h"
 
 #include "libgimp/libgimp-intl.h"
@@ -69,16 +70,27 @@ typedef enum
   COLOR_SELECT_HUE = 0,
   COLOR_SELECT_SATURATION,
   COLOR_SELECT_VALUE,
+
   COLOR_SELECT_RED,
   COLOR_SELECT_GREEN,
   COLOR_SELECT_BLUE,
   COLOR_SELECT_ALPHA,
+
+  COLOR_SELECT_LCH_LIGHTNESS,
+  COLOR_SELECT_LCH_CHROMA,
+  COLOR_SELECT_LCH_HUE,
+
   COLOR_SELECT_HUE_SATURATION,
   COLOR_SELECT_HUE_VALUE,
   COLOR_SELECT_SATURATION_VALUE,
+
   COLOR_SELECT_RED_GREEN,
   COLOR_SELECT_RED_BLUE,
-  COLOR_SELECT_GREEN_BLUE
+  COLOR_SELECT_GREEN_BLUE,
+
+  COLOR_SELECT_LCH_HUE_CHROMA,
+  COLOR_SELECT_LCH_HUE_LIGHTNESS,
+  COLOR_SELECT_LCH_CHROMA_LIGHTNESS
 } ColorSelectFillType;
 
 typedef enum
@@ -98,6 +110,14 @@ typedef enum
 } ColorSelectDragMode;
 
 
+typedef struct _GimpLCH  GimpLCH;
+
+struct _GimpLCH
+{
+  gdouble l, c, h, a;
+};
+
+
 #define GIMP_COLOR_SELECT_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_COLOR_SELECT, 
GimpColorSelectClass))
 #define GIMP_IS_COLOR_SELECT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_COLOR_SELECT))
 #define GIMP_COLOR_SELECT_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_COLOR_SELECT, 
GimpColorSelectClass))
@@ -112,14 +132,28 @@ struct _GimpColorSelect
   GtkWidget           *toggle_box;
 
   GtkWidget           *xy_color;
+  ColorSelectFillType  xy_color_fill;
+  guchar              *xy_buf;
+  gint                 xy_width;
+  gint                 xy_height;
+  gint                 xy_rowstride;
+  gboolean             xy_needs_render;
+
   GtkWidget           *z_color;
+  ColorSelectFillType  z_color_fill;
+  guchar              *z_buf;
+  gint                 z_width;
+  gint                 z_height;
+  gint                 z_rowstride;
+  gboolean             z_needs_render;
 
   gdouble              pos[3];
 
-  ColorSelectFillType  z_color_fill;
-  ColorSelectFillType  xy_color_fill;
-
   ColorSelectDragMode  drag_mode;
+
+  GimpColorConfig     *config;
+  GimpColorTransform  *transform;
+  guchar               oog_color[3];
 };
 
 struct _GimpColorSelectClass
@@ -130,7 +164,7 @@ struct _GimpColorSelectClass
 
 typedef struct _ColorSelectFill ColorSelectFill;
 
-typedef void (* ColorSelectFillUpdateProc) (ColorSelectFill *color_select_fill);
+typedef void (* ColorSelectRenderFunc) (ColorSelectFill *color_select_fill);
 
 struct _ColorSelectFill
 {
@@ -140,11 +174,15 @@ struct _ColorSelectFill
   gint     height;
   GimpRGB  rgb;
   GimpHSV  hsv;
+  GimpLCH  lch;
+  guchar   oog_color[3];
 
-  ColorSelectFillUpdateProc update;
+  ColorSelectRenderFunc render_line;
 };
 
 
+static void   gimp_color_select_finalize        (GObject            *object);
+
 static void   gimp_color_select_togg_visible    (GimpColorSelector  *selector,
                                                  gboolean            visible);
 static void   gimp_color_select_togg_sensitive  (GimpColorSelector  *selector,
@@ -192,52 +230,92 @@ static gboolean   gimp_color_select_z_events    (GtkWidget          *widget,
                                                  GdkEvent           *event,
                                                  GimpColorSelect    *select);
 
-static void   gimp_color_select_image_fill      (GtkWidget          *widget,
+static void   gimp_color_select_render          (GtkWidget          *widget,
+                                                 guchar             *buf,
+                                                 gint                width,
+                                                 gint                height,
+                                                 gint                rowstride,
                                                  ColorSelectFillType fill_type,
                                                  const GimpHSV      *hsv,
-                                                 const GimpRGB      *rgb);
-
-static void   color_select_update_red              (ColorSelectFill *csf);
-static void   color_select_update_green            (ColorSelectFill *csf);
-static void   color_select_update_blue             (ColorSelectFill *csf);
-static void   color_select_update_hue              (ColorSelectFill *csf);
-static void   color_select_update_saturation       (ColorSelectFill *csf);
-static void   color_select_update_value            (ColorSelectFill *csf);
-static void   color_select_update_red_green        (ColorSelectFill *csf);
-static void   color_select_update_red_blue         (ColorSelectFill *csf);
-static void   color_select_update_green_blue       (ColorSelectFill *csf);
-static void   color_select_update_hue_saturation   (ColorSelectFill *csf);
-static void   color_select_update_hue_value        (ColorSelectFill *csf);
-static void   color_select_update_saturation_value (ColorSelectFill *csf);
+                                                 const GimpRGB      *rgb,
+                                                 const guchar       *oog_color);
+
+static void   color_select_render_red              (ColorSelectFill *csf);
+static void   color_select_render_green            (ColorSelectFill *csf);
+static void   color_select_render_blue             (ColorSelectFill *csf);
+
+static void   color_select_render_hue              (ColorSelectFill *csf);
+static void   color_select_render_saturation       (ColorSelectFill *csf);
+static void   color_select_render_value            (ColorSelectFill *csf);
+
+static void   color_select_render_lch_lightness    (ColorSelectFill *csf);
+static void   color_select_render_lch_chroma       (ColorSelectFill *csf);
+static void   color_select_render_lch_hue          (ColorSelectFill *csf);
+
+static void   color_select_render_red_green        (ColorSelectFill *csf);
+static void   color_select_render_red_blue         (ColorSelectFill *csf);
+static void   color_select_render_green_blue       (ColorSelectFill *csf);
+
+static void   color_select_render_hue_saturation   (ColorSelectFill *csf);
+static void   color_select_render_hue_value        (ColorSelectFill *csf);
+static void   color_select_render_saturation_value (ColorSelectFill *csf);
+
+static void   color_select_render_lch_chroma_lightness (ColorSelectFill *csf);
+static void   color_select_render_lch_hue_lightness    (ColorSelectFill *csf);
+static void   color_select_render_lch_hue_chroma       (ColorSelectFill *csf);
+
+static void   gimp_color_select_create_transform   (GimpColorSelect  *select);
+static void   gimp_color_select_destroy_transform  (GimpColorSelect  *select);
+static void   gimp_color_select_notify_config      (GimpColorConfig  *config,
+                                                    const GParamSpec *pspec,
+                                                    GimpColorSelect  *select);
 
 
 G_DEFINE_TYPE (GimpColorSelect, gimp_color_select, GIMP_TYPE_COLOR_SELECTOR)
 
 #define parent_class gimp_color_select_parent_class
 
-static const ColorSelectFillUpdateProc update_procs[] =
+static const ColorSelectRenderFunc render_funcs[] =
 {
-  color_select_update_hue,
-  color_select_update_saturation,
-  color_select_update_value,
-  color_select_update_red,
-  color_select_update_green,
-  color_select_update_blue,
+  color_select_render_hue,
+  color_select_render_saturation,
+  color_select_render_value,
+
+  color_select_render_red,
+  color_select_render_green,
+  color_select_render_blue,
   NULL, /* alpha */
-  color_select_update_hue_saturation,
-  color_select_update_hue_value,
-  color_select_update_saturation_value,
-  color_select_update_red_green,
-  color_select_update_red_blue,
-  color_select_update_green_blue,
+
+  color_select_render_lch_lightness,
+  color_select_render_lch_chroma,
+  color_select_render_lch_hue,
+
+  color_select_render_hue_saturation,
+  color_select_render_hue_value,
+  color_select_render_saturation_value,
+
+  color_select_render_red_green,
+  color_select_render_red_blue,
+  color_select_render_green_blue,
+
+  color_select_render_lch_hue_chroma,
+  color_select_render_lch_hue_lightness,
+  color_select_render_lch_chroma_lightness
 };
 
+static const Babl *fish_rgb_to_lch    = NULL;
+static const Babl *fish_lch_to_rgb    = NULL;
+static const Babl *fish_lch_to_rgb_u8 = NULL;
+
 
 static void
 gimp_color_select_class_init (GimpColorSelectClass *klass)
 {
+  GObjectClass           *object_class   = G_OBJECT_CLASS (klass);
   GimpColorSelectorClass *selector_class = GIMP_COLOR_SELECTOR_CLASS (klass);
 
+  object_class->finalize                = gimp_color_select_finalize;
+
   selector_class->name                  = "GIMP";
   selector_class->help_id               = "gimp-colorselector-gimp";
   selector_class->icon_name             = GIMP_ICON_WILBER;
@@ -246,13 +324,21 @@ gimp_color_select_class_init (GimpColorSelectClass *klass)
   selector_class->set_color             = gimp_color_select_set_color;
   selector_class->set_channel           = gimp_color_select_set_channel;
   selector_class->set_config            = gimp_color_select_set_config;
+
+  fish_rgb_to_lch    = babl_fish (babl_format ("R'G'B'A double"),
+                                  babl_format ("CIE LCH(ab) double"));
+  fish_lch_to_rgb    = babl_fish (babl_format ("CIE LCH(ab) double"),
+                                  babl_format ("R'G'B' double"));
+  fish_lch_to_rgb_u8 = babl_fish (babl_format ("CIE LCH(ab) double"),
+                                  babl_format ("R'G'B' u8"));
 }
 
 static void
 gimp_color_select_init (GimpColorSelect *select)
 {
-  GtkWidget *hbox;
-  GtkWidget *frame;
+  GimpColorSelector *selector = GIMP_COLOR_SELECTOR (select);
+  GtkWidget         *hbox;
+  GtkWidget         *frame;
 
   select->z_color_fill  = COLOR_SELECT_HUE;
   select->xy_color_fill = COLOR_SELECT_SATURATION_VALUE;
@@ -268,7 +354,8 @@ gimp_color_select_init (GimpColorSelect *select)
   gtk_box_pack_start (GTK_BOX (hbox), frame, TRUE, TRUE, 0);
   gtk_widget_show (frame);
 
-  select->xy_color = gimp_preview_area_new ();
+  select->xy_color = gtk_event_box_new ();
+  gtk_event_box_set_visible_window (GTK_EVENT_BOX (select->xy_color), FALSE);
   g_object_add_weak_pointer (G_OBJECT (select->xy_color),
                              (gpointer) &select->xy_color);
   gtk_widget_set_size_request (select->xy_color,
@@ -299,7 +386,8 @@ gimp_color_select_init (GimpColorSelect *select)
   gtk_box_pack_start (GTK_BOX (hbox), frame, FALSE, FALSE, 0);
   gtk_widget_show (frame);
 
-  select->z_color = gimp_preview_area_new ();
+  select->z_color = gtk_event_box_new ();
+  gtk_event_box_set_visible_window (GTK_EVENT_BOX (select->z_color), FALSE);
   g_object_add_weak_pointer (G_OBJECT (select->z_color),
                              (gpointer) &select->z_color);
   gtk_widget_set_size_request (select->z_color,
@@ -330,13 +418,16 @@ gimp_color_select_init (GimpColorSelect *select)
 
     enum_class = g_type_class_ref (GIMP_TYPE_COLOR_SELECTOR_CHANNEL);
 
-    for (channel = GIMP_COLOR_SELECTOR_HUE;
-         channel < GIMP_COLOR_SELECTOR_ALPHA;
+    for (channel =  GIMP_COLOR_SELECTOR_HUE;
+         channel <= GIMP_COLOR_SELECTOR_LCH_HUE;
          channel++)
       {
         GimpEnumDesc *enum_desc;
         GtkWidget    *button;
 
+        if (channel == GIMP_COLOR_SELECTOR_ALPHA)
+          continue;
+
         enum_desc = gimp_enum_get_desc (enum_class, channel);
 
         button = gtk_radio_button_new_with_mnemonic (group,
@@ -350,7 +441,7 @@ gimp_color_select_init (GimpColorSelect *select)
         g_object_set_data (G_OBJECT (button), "channel",
                            GINT_TO_POINTER (channel));
 
-        if (channel == GIMP_COLOR_SELECTOR_HUE)
+        if (channel == selector->channel)
           gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
 
         gimp_help_set_help_data (button, gettext (enum_desc->value_help), NULL);
@@ -365,6 +456,32 @@ gimp_color_select_init (GimpColorSelect *select)
 }
 
 static void
+gimp_color_select_finalize (GObject *object)
+{
+  GimpColorSelect *select = GIMP_COLOR_SELECT (object);
+
+  if (select->xy_buf)
+    {
+      g_free (select->xy_buf);
+      select->xy_buf       = NULL;
+      select->xy_width     = 0;
+      select->xy_height    = 0;
+      select->xy_rowstride = 0;
+    }
+
+  if (select->z_buf)
+    {
+      g_free (select->z_buf);
+      select->z_buf       = NULL;
+      select->z_width     = 0;
+      select->z_height    = 0;
+      select->z_rowstride = 0;
+    }
+
+  G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static void
 gimp_color_select_togg_visible (GimpColorSelector *selector,
                                 gboolean           visible)
 {
@@ -431,6 +548,21 @@ gimp_color_select_set_channel (GimpColorSelector        *selector,
       select->xy_color_fill = COLOR_SELECT_RED_GREEN;
       break;
 
+    case COLOR_SELECT_LCH_LIGHTNESS:
+      select->z_color_fill  = COLOR_SELECT_LCH_LIGHTNESS;
+      select->xy_color_fill = COLOR_SELECT_LCH_HUE_CHROMA;
+      break;
+
+    case COLOR_SELECT_LCH_CHROMA:
+      select->z_color_fill  = COLOR_SELECT_LCH_CHROMA;
+      select->xy_color_fill = COLOR_SELECT_LCH_HUE_LIGHTNESS;
+      break;
+
+     case COLOR_SELECT_LCH_HUE:
+      select->z_color_fill  = COLOR_SELECT_LCH_HUE;
+      select->xy_color_fill = COLOR_SELECT_LCH_CHROMA_LIGHTNESS;
+      break;
+
     default:
       break;
     }
@@ -445,13 +577,31 @@ gimp_color_select_set_config (GimpColorSelector *selector,
 {
   GimpColorSelect *select = GIMP_COLOR_SELECT (selector);
 
-  if (select->xy_color)
-    gimp_preview_area_set_color_config (GIMP_PREVIEW_AREA (select->xy_color),
-                                        config);
+  if (config != select->config)
+    {
+      if (select->config)
+        {
+          g_signal_handlers_disconnect_by_func (select->config,
+                                                gimp_color_select_notify_config,
+                                                select);
+          g_object_unref (select->config);
+
+          gimp_color_select_destroy_transform (select);
+        }
+
+      select->config = config;
+
+      if (select->config)
+        {
+          g_object_ref (select->config);
 
-  if (select->z_color)
-    gimp_preview_area_set_color_config (GIMP_PREVIEW_AREA (select->z_color),
-                                        config);
+          g_signal_connect (select->config, "notify",
+                            G_CALLBACK (gimp_color_select_notify_config),
+                            select);
+
+          gimp_color_select_notify_config (select->config, NULL, select);
+        }
+    }
 }
 
 static void
@@ -477,8 +627,6 @@ static void
 gimp_color_select_update (GimpColorSelect       *select,
                           ColorSelectUpdateType  update)
 {
-  GimpColorSelector *selector = GIMP_COLOR_SELECTOR (select);
-
   if (update & UPDATE_POS)
     gimp_color_select_update_pos (select);
 
@@ -487,15 +635,13 @@ gimp_color_select_update (GimpColorSelect       *select,
 
   if (update & UPDATE_XY_COLOR)
     {
-      gimp_color_select_image_fill (select->xy_color, select->xy_color_fill,
-                                    &selector->hsv, &selector->rgb);
+      select->xy_needs_render = TRUE;
       gtk_widget_queue_draw (select->xy_color);
     }
 
   if (update & UPDATE_Z_COLOR)
     {
-      gimp_color_select_image_fill (select->z_color, select->z_color_fill,
-                                    &selector->hsv, &selector->rgb);
+      select->z_needs_render = TRUE;
       gtk_widget_queue_draw (select->z_color);
     }
 
@@ -507,6 +653,7 @@ static void
 gimp_color_select_update_values (GimpColorSelect *select)
 {
   GimpColorSelector *selector = GIMP_COLOR_SELECTOR (select);
+  GimpLCH            lch;
 
   switch (select->z_color_fill)
     {
@@ -542,6 +689,22 @@ gimp_color_select_update_values (GimpColorSelect *select)
       selector->hsv.v = select->pos[2];
       break;
 
+    case COLOR_SELECT_LCH_LIGHTNESS:
+      lch.h = select->pos[0] * 360;
+      lch.c = select->pos[1] * 100;
+      lch.l = select->pos[2] * 100;
+      break;
+    case COLOR_SELECT_LCH_CHROMA:
+      lch.h = select->pos[0] * 360;
+      lch.l = select->pos[1] * 100;
+      lch.c = select->pos[2] * 100;
+      break;
+    case COLOR_SELECT_LCH_HUE:
+      lch.c = select->pos[0] * 100;
+      lch.l = select->pos[1] * 100;
+      lch.h = select->pos[2] * 360;
+      break;
+
     default:
       break;
     }
@@ -560,6 +723,13 @@ gimp_color_select_update_values (GimpColorSelect *select)
       gimp_hsv_to_rgb (&selector->hsv, &selector->rgb);
       break;
 
+    case COLOR_SELECT_LCH_LIGHTNESS:
+    case COLOR_SELECT_LCH_CHROMA:
+    case COLOR_SELECT_LCH_HUE:
+      babl_process (fish_lch_to_rgb, &lch, &selector->rgb, 1);
+      gimp_rgb_to_hsv (&selector->rgb, &selector->hsv);
+      break;
+
     default:
       break;
     }
@@ -569,6 +739,9 @@ static void
 gimp_color_select_update_pos (GimpColorSelect *select)
 {
   GimpColorSelector *selector = GIMP_COLOR_SELECTOR (select);
+  GimpLCH            lch;
+
+  babl_process (fish_rgb_to_lch, &selector->rgb, &lch, 1);
 
   switch (select->z_color_fill)
     {
@@ -604,6 +777,22 @@ gimp_color_select_update_pos (GimpColorSelect *select)
       select->pos[2] = CLAMP (selector->hsv.v, 0.0, 1.0);
       break;
 
+    case COLOR_SELECT_LCH_LIGHTNESS:
+      select->pos[0] = CLAMP (lch.h / 360, 0.0, 1.0);
+      select->pos[1] = CLAMP (lch.c / 100, 0.0, 1.0);
+      select->pos[2] = CLAMP (lch.l / 100, 0.0, 1.0);
+      break;
+    case COLOR_SELECT_LCH_CHROMA:
+      select->pos[0] = CLAMP (lch.h / 360, 0.0, 1.0);
+      select->pos[1] = CLAMP (lch.l / 100, 0.0, 1.0);
+      select->pos[2] = CLAMP (lch.c / 100, 0.0, 1.0);
+      break;
+    case COLOR_SELECT_LCH_HUE:
+      select->pos[0] = CLAMP (lch.c / 100, 0.0, 1.0);
+      select->pos[1] = CLAMP (lch.l / 100, 0.0, 1.0);
+      select->pos[2] = CLAMP (lch.h / 360, 0.0, 1.0);
+      break;
+
     default:
       break;
     }
@@ -622,6 +811,7 @@ gimp_color_select_drop_color (GtkWidget     *widget,
   select->rgb = *color;
 
   gimp_color_select_update_hsv_values (select);
+  gimp_color_select_update_lch_values (select);
 
   gimp_color_select_update (select,
                             UPDATE_POS | UPDATE_XY_COLOR | UPDATE_Z_COLOR |
@@ -634,6 +824,20 @@ gimp_color_select_xy_size_allocate (GtkWidget       *widget,
                                     GtkAllocation   *allocation,
                                     GimpColorSelect *select)
 {
+  if (allocation->width  != select->xy_width ||
+      allocation->height != select->xy_height)
+    {
+      select->xy_width  = allocation->width;
+      select->xy_height = allocation->height;
+
+      select->xy_rowstride = (select->xy_width * 3 + 3) & ~3;
+
+      g_free (select->xy_buf);
+      select->xy_buf = g_new (guchar, select->xy_rowstride * select->xy_height);
+
+      select->xy_needs_render = TRUE;
+    }
+
   gimp_color_select_update (select, UPDATE_XY_COLOR);
 }
 
@@ -644,14 +848,84 @@ gimp_color_select_xy_expose (GtkWidget       *widget,
 {
   GtkAllocation  allocation;
   cairo_t       *cr;
+  GdkPixbuf     *pixbuf;
   gint           x, y;
 
+  if (! select->xy_buf)
+    return FALSE;
+
+  if (select->xy_needs_render)
+    {
+      GimpColorSelector *selector = GIMP_COLOR_SELECTOR (select);
+
+      gimp_color_select_render (select->xy_color,
+                                select->xy_buf,
+                                select->xy_width,
+                                select->xy_height,
+                                select->xy_rowstride,
+                                select->xy_color_fill,
+                                &selector->hsv,
+                                &selector->rgb,
+                                select->oog_color);
+      select->xy_needs_render = FALSE;
+    }
+
+  if (! select->transform)
+    gimp_color_select_create_transform (select);
+
+  if (select->transform)
+    {
+      const Babl *format = babl_format ("R'G'B' u8");
+      guchar     *buf    = g_new (guchar,
+                                  select->xy_rowstride * select->xy_height);
+      guchar     *src    = select->xy_buf;
+      guchar     *dest   = buf;
+      gint        i;
+
+      for (i = 0; i < select->xy_height; i++)
+        {
+          gimp_color_transform_process_pixels (select->transform,
+                                               format, src,
+                                               format, dest,
+                                               select->xy_width);
+
+          src  += select->xy_rowstride;
+          dest += select->xy_rowstride;
+        }
+
+      pixbuf = gdk_pixbuf_new_from_data (buf,
+                                         GDK_COLORSPACE_RGB,
+                                         FALSE,
+                                         8,
+                                         select->xy_width,
+                                         select->xy_height,
+                                         select->xy_rowstride,
+                                         (GdkPixbufDestroyNotify) g_free, NULL);
+    }
+  else
+    {
+      pixbuf = gdk_pixbuf_new_from_data (select->xy_buf,
+                                         GDK_COLORSPACE_RGB,
+                                         FALSE,
+                                         8,
+                                         select->xy_width,
+                                         select->xy_height,
+                                         select->xy_rowstride,
+                                         NULL, NULL);
+    }
+
   gtk_widget_get_allocation (select->xy_color, &allocation);
 
   cr = gdk_cairo_create (gtk_widget_get_window (widget));
   gdk_cairo_region (cr, event->region);
   cairo_clip (cr);
 
+  cairo_translate (cr, allocation.x, allocation.y);
+
+  gdk_cairo_set_source_pixbuf (cr, pixbuf, 0, 0);
+  g_object_unref (pixbuf);
+  cairo_paint (cr);
+
   x = (allocation.width  - 1) * select->pos[0];
   y = (allocation.height - 1) - (allocation.height - 1) * select->pos[1];
 
@@ -756,6 +1030,20 @@ gimp_color_select_z_size_allocate (GtkWidget       *widget,
                                    GtkAllocation   *allocation,
                                    GimpColorSelect *select)
 {
+  if (allocation->width  != select->z_width ||
+      allocation->height != select->z_height)
+    {
+      select->z_width  = allocation->width;
+      select->z_height = allocation->height;
+
+      select->z_rowstride = (select->z_width * 3 + 3) & ~3;
+
+      g_free (select->z_buf);
+      select->z_buf = g_new (guchar, select->z_rowstride * select->z_height);
+
+      select->z_needs_render = TRUE;
+    }
+
   gimp_color_select_update (select, UPDATE_Z_COLOR);
 }
 
@@ -766,14 +1054,84 @@ gimp_color_select_z_expose (GtkWidget       *widget,
 {
   GtkAllocation  allocation;
   cairo_t       *cr;
+  GdkPixbuf     *pixbuf;
   gint           y;
 
+  if (! select->z_buf)
+    return FALSE;
+
+  if (select->z_needs_render)
+    {
+      GimpColorSelector *selector = GIMP_COLOR_SELECTOR (select);
+
+      gimp_color_select_render (select->z_color,
+                                select->z_buf,
+                                select->z_width,
+                                select->z_height,
+                                select->z_rowstride,
+                                select->z_color_fill,
+                                &selector->hsv,
+                                &selector->rgb,
+                                select->oog_color);
+      select->z_needs_render = FALSE;
+    }
+
   gtk_widget_get_allocation (widget, &allocation);
 
   cr = gdk_cairo_create (gtk_widget_get_window (widget));
   gdk_cairo_region (cr, event->region);
   cairo_clip (cr);
 
+  if (! select->transform)
+    gimp_color_select_create_transform (select);
+
+  if (select->transform)
+    {
+      const Babl *format = babl_format ("R'G'B' u8");
+      guchar     *buf    = g_new (guchar,
+                                  select->z_rowstride * select->z_height);
+      guchar     *src    = select->z_buf;
+      guchar     *dest   = buf;
+      gint        i;
+
+      for (i = 0; i < select->z_height; i++)
+        {
+          gimp_color_transform_process_pixels (select->transform,
+                                               format, src,
+                                               format, dest,
+                                               select->z_width);
+
+          src  += select->z_rowstride;
+          dest += select->z_rowstride;
+        }
+
+      pixbuf = gdk_pixbuf_new_from_data (buf,
+                                         GDK_COLORSPACE_RGB,
+                                         FALSE,
+                                         8,
+                                         select->z_width,
+                                         select->z_height,
+                                         select->z_rowstride,
+                                         (GdkPixbufDestroyNotify) g_free, NULL);
+    }
+  else
+    {
+      pixbuf = gdk_pixbuf_new_from_data (select->z_buf,
+                                         GDK_COLORSPACE_RGB,
+                                         FALSE,
+                                         8,
+                                         select->z_width,
+                                         select->z_height,
+                                         select->z_rowstride,
+                                         NULL, NULL);
+    }
+
+  cairo_translate (cr, allocation.x, allocation.y);
+
+  gdk_cairo_set_source_pixbuf (cr, pixbuf, 0, 0);
+  g_object_unref (pixbuf);
+  cairo_paint (cr);
+
   y = (allocation.height - 1) - (allocation.height - 1) * select->pos[2];
 
   cairo_move_to (cr, 0,                y + 0.5);
@@ -864,37 +1222,42 @@ gimp_color_select_z_events (GtkWidget       *widget,
 }
 
 static void
-gimp_color_select_image_fill (GtkWidget           *preview,
-                              ColorSelectFillType  fill_type,
-                              const GimpHSV       *hsv,
-                              const GimpRGB       *rgb)
+gimp_color_select_render (GtkWidget           *preview,
+                          guchar              *buf,
+                          gint                 width,
+                          gint                 height,
+                          gint                 rowstride,
+                          ColorSelectFillType  fill_type,
+                          const GimpHSV       *hsv,
+                          const GimpRGB       *rgb,
+                          const guchar        *oog_color)
 {
-  GtkAllocation   allocation;
-  ColorSelectFill csf;
+  ColorSelectFill  csf;
 
-  gtk_widget_get_allocation (preview, &allocation);
+  csf.width       = width;
+  csf.height      = height;
+  csf.hsv         = *hsv;
+  csf.rgb         = *rgb;
+  csf.render_line = render_funcs[fill_type];
 
-  csf.buffer = g_alloca (allocation.width * 3);
-  csf.width  = allocation.width;
-  csf.height = allocation.height;
-  csf.hsv    = *hsv;
-  csf.rgb    = *rgb;
-  csf.update = update_procs[fill_type];
+  csf.oog_color[0] = oog_color[0];
+  csf.oog_color[1] = oog_color[1];
+  csf.oog_color[2] = oog_color[2];
+
+  babl_process (fish_rgb_to_lch, rgb, &csf.lch, 1);
 
   for (csf.y = 0; csf.y < csf.height; csf.y++)
     {
-      if (csf.update)
-        (* csf.update) (&csf);
+      csf.buffer = buf;
+
+      csf.render_line (&csf);
 
-      gimp_preview_area_draw (GIMP_PREVIEW_AREA (preview),
-                              0, csf.y, csf.width, 1,
-                              GIMP_RGB_IMAGE,
-                              csf.buffer, csf.width * 3);
+      buf += rowstride;
     }
 }
 
 static void
-color_select_update_red (ColorSelectFill *csf)
+color_select_render_red (ColorSelectFill *csf)
 {
   guchar *p = csf->buffer;
   gint    i, r;
@@ -911,7 +1274,7 @@ color_select_update_red (ColorSelectFill *csf)
 }
 
 static void
-color_select_update_green (ColorSelectFill *csf)
+color_select_render_green (ColorSelectFill *csf)
 {
   guchar *p = csf->buffer;
   gint    i, g;
@@ -928,7 +1291,7 @@ color_select_update_green (ColorSelectFill *csf)
 }
 
 static void
-color_select_update_blue (ColorSelectFill *csf)
+color_select_render_blue (ColorSelectFill *csf)
 {
   guchar *p = csf->buffer;
   gint    i, b;
@@ -945,7 +1308,7 @@ color_select_update_blue (ColorSelectFill *csf)
 }
 
 static void
-color_select_update_hue (ColorSelectFill *csf)
+color_select_render_hue (ColorSelectFill *csf)
 {
   guchar *p = csf->buffer;
   gfloat  h, f;
@@ -1003,7 +1366,7 @@ color_select_update_hue (ColorSelectFill *csf)
 }
 
 static void
-color_select_update_saturation (ColorSelectFill *csf)
+color_select_render_saturation (ColorSelectFill *csf)
 {
   guchar *p = csf->buffer;
   gint    s;
@@ -1023,7 +1386,7 @@ color_select_update_saturation (ColorSelectFill *csf)
 }
 
 static void
-color_select_update_value (ColorSelectFill *csf)
+color_select_render_value (ColorSelectFill *csf)
 {
   guchar *p = csf->buffer;
   gint    v;
@@ -1043,7 +1406,64 @@ color_select_update_value (ColorSelectFill *csf)
 }
 
 static void
-color_select_update_red_green (ColorSelectFill *csf)
+color_select_render_lch_lightness (ColorSelectFill *csf)
+{
+  guchar  *p   = csf->buffer;
+  GimpLCH  lch = { 0.0, 0.0, 0.0, 1.0 };
+  guchar   rgb[3];
+  gint     i;
+
+  lch.l = (csf->height - 1 - csf->y) * 100.0 / csf->height;
+  babl_process (fish_lch_to_rgb_u8, &lch, &rgb, 1);
+
+  for (i = 0; i < csf->width; i++)
+    {
+      *p++ = rgb[0];
+      *p++ = rgb[1];
+      *p++ = rgb[2];
+    }
+}
+
+static void
+color_select_render_lch_chroma (ColorSelectFill *csf)
+{
+  guchar  *p   = csf->buffer;
+  GimpLCH  lch = { 80.0, 0.0, 0.0, 1.0 };
+  guchar   rgb[3];
+  gint     i;
+
+  lch.c = (csf->height - 1 - csf->y) * 100.0 / csf->height ;
+  babl_process (fish_lch_to_rgb_u8, &lch, &rgb, 1);
+
+  for (i = 0; i < csf->width; i++)
+    {
+      *p++ = rgb[0];
+      *p++ = rgb[1];
+      *p++ = rgb[2];
+    }
+}
+
+static void
+color_select_render_lch_hue (ColorSelectFill *csf)
+{
+  guchar  *p   = csf->buffer;
+  GimpLCH  lch = { 80.0, 200.0, 0.0, 1.0 };
+  guchar   rgb[3];
+  gint     i;
+
+  lch.h = (csf->height - 1 - csf->y) * 360.0 / csf->height;
+  babl_process (fish_lch_to_rgb_u8, &lch, &rgb, 1);
+
+  for (i = 0; i < csf->width; i++)
+    {
+      *p++ = rgb[0];
+      *p++ = rgb[1];
+      *p++ = rgb[2];
+    }
+}
+
+static void
+color_select_render_red_green (ColorSelectFill *csf)
 {
   guchar *p = csf->buffer;
   gint    i, g, b;
@@ -1068,7 +1488,7 @@ color_select_update_red_green (ColorSelectFill *csf)
 }
 
 static void
-color_select_update_red_blue (ColorSelectFill *csf)
+color_select_render_red_blue (ColorSelectFill *csf)
 {
   guchar *p = csf->buffer;
   gint    i, g, b;
@@ -1093,7 +1513,7 @@ color_select_update_red_blue (ColorSelectFill *csf)
 }
 
 static void
-color_select_update_green_blue (ColorSelectFill *csf)
+color_select_render_green_blue (ColorSelectFill *csf)
 {
   guchar *p = csf->buffer;
   gint    i, r, b;
@@ -1118,7 +1538,7 @@ color_select_update_green_blue (ColorSelectFill *csf)
 }
 
 static void
-color_select_update_hue_saturation (ColorSelectFill *csf)
+color_select_render_hue_saturation (ColorSelectFill *csf)
 {
   guchar *p = csf->buffer;
   gfloat  h, dh, s, v;
@@ -1177,7 +1597,7 @@ color_select_update_hue_saturation (ColorSelectFill *csf)
 }
 
 static void
-color_select_update_hue_value (ColorSelectFill *csf)
+color_select_render_hue_value (ColorSelectFill *csf)
 {
   guchar *p = csf->buffer;
   gfloat  h, dh, s, v;
@@ -1236,7 +1656,7 @@ color_select_update_hue_value (ColorSelectFill *csf)
 }
 
 static void
-color_select_update_saturation_value (ColorSelectFill *csf)
+color_select_render_saturation_value (ColorSelectFill *csf)
 {
   guchar *p = csf->buffer;
   gfloat  h, s, ds, v;
@@ -1320,3 +1740,156 @@ color_select_update_saturation_value (ColorSelectFill *csf)
       break;
     }
 }
+
+static void
+color_select_render_lch_chroma_lightness (ColorSelectFill *csf)
+{
+  guchar  *p = csf->buffer;
+  GimpLCH  lch;
+  gint     i;
+
+  lch.l = (csf->height - 1 - csf->y) * 100.0 / csf->height;
+  lch.h = csf->lch.h;
+
+  for (i = 0; i < csf->width; i++)
+    {
+      GimpRGB rgb;
+
+      lch.c = i * 100.0 / csf->width;
+
+      babl_process (fish_lch_to_rgb, &lch, &rgb, 1);
+
+      if (rgb.r < 0.0 || rgb.r > 1.0 ||
+          rgb.g < 0.0 || rgb.g > 1.0 ||
+          rgb.b < 0.0 || rgb.b > 1.0)
+        {
+          p[0] = csf->oog_color[0];
+          p[1] = csf->oog_color[1];
+          p[2] = csf->oog_color[2];
+        }
+      else
+        {
+          gimp_rgb_get_uchar (&rgb, p, p + 1, p + 2);
+        }
+
+      p += 3;
+    }
+}
+
+static void
+color_select_render_lch_hue_lightness (ColorSelectFill *csf)
+{
+  guchar  *p = csf->buffer;
+  GimpLCH  lch;
+  gint     i;
+
+  lch.l = (csf->height - 1 - csf->y) * 100.0 / csf->height;
+  lch.c = csf->lch.c;
+
+  for (i = 0; i < csf->width; i++)
+    {
+      GimpRGB rgb;
+
+      lch.h = i * 360.0 / csf->width;
+
+      babl_process (fish_lch_to_rgb, &lch, &rgb, 1);
+
+      if (rgb.r < 0.0 || rgb.r > 1.0 ||
+          rgb.g < 0.0 || rgb.g > 1.0 ||
+          rgb.b < 0.0 || rgb.b > 1.0)
+        {
+          p[0] = csf->oog_color[0];
+          p[1] = csf->oog_color[1];
+          p[2] = csf->oog_color[2];
+        }
+      else
+        {
+          gimp_rgb_get_uchar (&rgb, p, p + 1, p + 2);
+        }
+
+      p += 3;
+    }
+}
+
+static void
+color_select_render_lch_hue_chroma (ColorSelectFill *csf)
+{
+  guchar  *p = csf->buffer;
+  GimpLCH  lch;
+  gint     i;
+
+  lch.l = csf->lch.l;
+  lch.c = (csf->height - 1 - csf->y) * 100.0 / csf->height;
+
+  for (i = 0; i < csf->width; i++)
+    {
+      GimpRGB rgb;
+
+      lch.h = i * 360.0 / csf->width;
+
+      babl_process (fish_lch_to_rgb, &lch, &rgb, 1);
+
+      if (rgb.r < 0.0 || rgb.r > 1.0 ||
+          rgb.g < 0.0 || rgb.g > 1.0 ||
+          rgb.b < 0.0 || rgb.b > 1.0)
+        {
+          p[0] = csf->oog_color[0];
+          p[1] = csf->oog_color[1];
+          p[2] = csf->oog_color[2];
+        }
+      else
+        {
+          gimp_rgb_get_uchar (&rgb, p, p + 1, p + 2);
+        }
+
+      p += 3;
+    }
+}
+
+static void
+gimp_color_select_create_transform (GimpColorSelect *select)
+{
+  if (select->config)
+    {
+      static GimpColorProfile *profile = NULL;
+
+      const Babl *format = babl_format ("cairo-RGB24");
+
+      if (G_UNLIKELY (! profile))
+        profile = gimp_color_profile_new_rgb_srgb ();
+
+      select->transform = gimp_widget_get_color_transform (GTK_WIDGET (select),
+                                                           select->config,
+                                                           profile,
+                                                           format,
+                                                           format);
+    }
+}
+
+static void
+gimp_color_select_destroy_transform (GimpColorSelect *select)
+{
+  if (select->transform)
+    {
+      g_object_unref (select->transform);
+      select->transform = NULL;
+    }
+
+  gtk_widget_queue_draw (select->xy_color);
+  gtk_widget_queue_draw (select->z_color);
+}
+
+static void
+gimp_color_select_notify_config (GimpColorConfig  *config,
+                                 const GParamSpec *pspec,
+                                 GimpColorSelect  *select)
+{
+  gimp_color_select_destroy_transform (select);
+
+  gimp_rgb_get_uchar (&config->out_of_gamut_color,
+                      select->oog_color,
+                      select->oog_color + 1,
+                      select->oog_color + 2);
+  select->xy_needs_render = TRUE;
+  select->z_needs_render  = TRUE;
+}
diff --git a/libgimpwidgets/gimpwidgetsenums.c b/libgimpwidgets/gimpwidgetsenums.c
index fa6bfff..829c284 100644
--- a/libgimpwidgets/gimpwidgetsenums.c
+++ b/libgimpwidgets/gimpwidgetsenums.c
@@ -118,18 +118,24 @@ gimp_color_selector_channel_get_type (void)
     { GIMP_COLOR_SELECTOR_GREEN, "GIMP_COLOR_SELECTOR_GREEN", "green" },
     { GIMP_COLOR_SELECTOR_BLUE, "GIMP_COLOR_SELECTOR_BLUE", "blue" },
     { GIMP_COLOR_SELECTOR_ALPHA, "GIMP_COLOR_SELECTOR_ALPHA", "alpha" },
+    { GIMP_COLOR_SELECTOR_LCH_LIGHTNESS, "GIMP_COLOR_SELECTOR_LCH_LIGHTNESS", "lch-lightness" },
+    { GIMP_COLOR_SELECTOR_LCH_CHROMA, "GIMP_COLOR_SELECTOR_LCH_CHROMA", "lch-chroma" },
+    { GIMP_COLOR_SELECTOR_LCH_HUE, "GIMP_COLOR_SELECTOR_LCH_HUE", "lch-hue" },
     { 0, NULL, NULL }
   };
 
   static const GimpEnumDesc descs[] =
   {
-    { GIMP_COLOR_SELECTOR_HUE, NC_("color-selector-channel", "_H"), N_("Hue") },
-    { GIMP_COLOR_SELECTOR_SATURATION, NC_("color-selector-channel", "_S"), N_("Saturation") },
-    { GIMP_COLOR_SELECTOR_VALUE, NC_("color-selector-channel", "_V"), N_("Value") },
+    { GIMP_COLOR_SELECTOR_HUE, NC_("color-selector-channel", "_H"), N_("HSV Hue") },
+    { GIMP_COLOR_SELECTOR_SATURATION, NC_("color-selector-channel", "_S"), N_("HSV Saturation") },
+    { GIMP_COLOR_SELECTOR_VALUE, NC_("color-selector-channel", "_V"), N_("HSV Value") },
     { GIMP_COLOR_SELECTOR_RED, NC_("color-selector-channel", "_R"), N_("Red") },
     { GIMP_COLOR_SELECTOR_GREEN, NC_("color-selector-channel", "_G"), N_("Green") },
     { GIMP_COLOR_SELECTOR_BLUE, NC_("color-selector-channel", "_B"), N_("Blue") },
     { GIMP_COLOR_SELECTOR_ALPHA, NC_("color-selector-channel", "_A"), N_("Alpha") },
+    { GIMP_COLOR_SELECTOR_LCH_LIGHTNESS, NC_("color-selector-channel", "_L"), N_("LCH Lightness") },
+    { GIMP_COLOR_SELECTOR_LCH_CHROMA, NC_("color-selector-channel", "_C"), N_("LCH Chroma") },
+    { GIMP_COLOR_SELECTOR_LCH_HUE, NC_("color-selector-channel", "_H"), N_("LCH Hue") },
     { 0, NULL, NULL }
   };
 
diff --git a/libgimpwidgets/gimpwidgetsenums.h b/libgimpwidgets/gimpwidgetsenums.h
index a93c24d..0d8ca48 100644
--- a/libgimpwidgets/gimpwidgetsenums.h
+++ b/libgimpwidgets/gimpwidgetsenums.h
@@ -89,13 +89,16 @@ typedef enum
 
 /**
  * GimpColorSelectorChannel:
- * @GIMP_COLOR_SELECTOR_HUE:        the hue channel
- * @GIMP_COLOR_SELECTOR_SATURATION: the saturation channel
- * @GIMP_COLOR_SELECTOR_VALUE:      the value channel
- * @GIMP_COLOR_SELECTOR_RED:        the red channel
- * @GIMP_COLOR_SELECTOR_GREEN:      the green channel
- * @GIMP_COLOR_SELECTOR_BLUE:       the blue channel
- * @GIMP_COLOR_SELECTOR_ALPHA:      the alpha channel
+ * @GIMP_COLOR_SELECTOR_HUE:            the hue channel
+ * @GIMP_COLOR_SELECTOR_SATURATION:     the saturation channel
+ * @GIMP_COLOR_SELECTOR_VALUE:          the value channel
+ * @GIMP_COLOR_SELECTOR_RED:            the red channel
+ * @GIMP_COLOR_SELECTOR_GREEN:          the green channel
+ * @GIMP_COLOR_SELECTOR_BLUE:           the blue channel
+ * @GIMP_COLOR_SELECTOR_ALPHA:          the alpha channel
+ * @GIMP_COLOR_SELECTOR_LCH_LIGHTNESS:  the lightness channel
+ * @GIMP_COLOR_SELECTOR_LCH_CHOMA:      the chroma channel
+ * @GIMP_COLOR_SELECTOR_LCH_HUE:        the hue channel
  *
  * An enum to specify the types of color channels edited in
  * #GimpColorSelector widgets.
@@ -106,13 +109,16 @@ GType gimp_color_selector_channel_get_type (void) G_GNUC_CONST;
 
 typedef enum
 {
-  GIMP_COLOR_SELECTOR_HUE,        /*< desc="_H", help="Hue"        >*/
-  GIMP_COLOR_SELECTOR_SATURATION, /*< desc="_S", help="Saturation" >*/
-  GIMP_COLOR_SELECTOR_VALUE,      /*< desc="_V", help="Value"      >*/
-  GIMP_COLOR_SELECTOR_RED,        /*< desc="_R", help="Red"        >*/
-  GIMP_COLOR_SELECTOR_GREEN,      /*< desc="_G", help="Green"      >*/
-  GIMP_COLOR_SELECTOR_BLUE,       /*< desc="_B", help="Blue"       >*/
-  GIMP_COLOR_SELECTOR_ALPHA       /*< desc="_A", help="Alpha"      >*/
+  GIMP_COLOR_SELECTOR_HUE,           /*< desc="_H", help="HSV Hue"        >*/
+  GIMP_COLOR_SELECTOR_SATURATION,    /*< desc="_S", help="HSV Saturation" >*/
+  GIMP_COLOR_SELECTOR_VALUE,         /*< desc="_V", help="HSV Value"      >*/
+  GIMP_COLOR_SELECTOR_RED,           /*< desc="_R", help="Red"            >*/
+  GIMP_COLOR_SELECTOR_GREEN,         /*< desc="_G", help="Green"          >*/
+  GIMP_COLOR_SELECTOR_BLUE,          /*< desc="_B", help="Blue"           >*/
+  GIMP_COLOR_SELECTOR_ALPHA,         /*< desc="_A", help="Alpha"          >*/
+  GIMP_COLOR_SELECTOR_LCH_LIGHTNESS, /*< desc="_L", help="LCH Lightness"  >*/
+  GIMP_COLOR_SELECTOR_LCH_CHROMA,    /*< desc="_C", help="LCH Chroma"     >*/
+  GIMP_COLOR_SELECTOR_LCH_HUE        /*< desc="_H", help="LCH Hue"        >*/
 } GimpColorSelectorChannel;
 
 


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