Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Gimp/libgimpwidgets/   (Gimp Bildeditor Version 3.2.0©)  Datei vom 14.2.2026 mit Größe 64 kB image not shown  

Quelle  gimpcolorselect.c   Sprache: C

 

/* LIBGIMP - The GIMP Library
 * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
 *
 * gimpcolorselect.c
 * Copyright (C) 2002 Michael Natterer <mitch@gimp.org>
 *
 * based on color_notebook module
 * Copyright (C) 1998 Austin Donnelly <austin@greenend.org.uk>
 *
 * This library is free software: you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 3 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library.  If not, see
 * <https://www.gnu.org/licenses/>.
 */


#include "config.h"

#include <gegl.h>
#include <gtk/gtk.h>

#include "libgimpbase/gimpbase.h"
#include "libgimpconfig/gimpconfig.h"
#include "libgimpcolor/gimpcolor.h"
#include "libgimpmath/gimpmath.h"

#include "gimpwidgetstypes.h"

#include "gimpcolorselector.h"
#include "gimpcolorselect.h"
#include "gimphelpui.h"
#include "gimpicons.h"
#include "gimpwidgetsutils.h"

#include "libgimp/libgimp-intl.h"


/**
 * SECTION: gimpcolorselect
 * @title: GimpColorSelect
 * @short_description: A #GimpColorSelector implementation.
 *
 * The #GimpColorSelect widget is an implementation of a
 * #GimpColorSelector. It shows a square area that supports
 * interactively changing two color channels and a smaller area to
 * change the third channel. You can select which channel should be
 * the third by calling gimp_color_selector_set_channel(). The widget
 * will then change the other two channels accordingly.
 **/



#define COLOR_AREA_EVENT_MASK (GDK_EXPOSURE_MASK       | \
                               GDK_BUTTON_PRESS_MASK   | \
                               GDK_BUTTON_RELEASE_MASK | \
                               GDK_BUTTON_MOTION_MASK  | \
                               GDK_ENTER_NOTIFY_MASK)


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_LCH_HUE_CHROMA,
  COLOR_SELECT_LCH_HUE_LIGHTNESS,
  COLOR_SELECT_LCH_CHROMA_LIGHTNESS
} ColorSelectFillType;

typedef enum
{
  UPDATE_VALUES     = 1 << 0,
  UPDATE_POS        = 1 << 1,
  UPDATE_XY_COLOR   = 1 << 2,
  UPDATE_Z_COLOR    = 1 << 3,
  UPDATE_CALLER     = 1 << 6
} ColorSelectUpdateType;

typedef enum
{
  DRAG_NONE,
  DRAG_XY,
  DRAG_Z
} ColorSelectDragMode;


typedef struct _GimpLCH  GimpLCH;

struct _GimpLCH
{
  gdouble l, c, h, a;
};


struct _GimpColorSelect
{
  GimpColorSelector    parent_instance;

  GtkWidget           *toggle_box[3];
  GtkWidget           *label;
  GtkWidget           *simulation_label;

  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];
  gfloat               z_value;

  ColorSelectDragMode  drag_mode;

  GimpColorConfig     *config;
  const Babl          *format;
  guchar               oog_color[3];
};

struct _GimpColorSelectClass
{
  GimpColorSelectorClass  parent_class;
};


typedef struct _ColorSelectFill ColorSelectFill;

typedef void (* ColorSelectRenderFunc) (ColorSelectFill *color_select_fill);

struct _ColorSelectFill
{
  guchar  *buffer;
  gint     y;
  gint     width;
  gint     height;
  gfloat   rgb[3];
  gfloat   hsv[3];
  gfloat   lch[4];
  guchar   oog_color[3];

  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,
                                                            gboolean                  sensitive);
static void       gimp_color_select_set_color              (GimpColorSelector        *selector,
                                                            GeglColor                *color);
static void       gimp_color_select_set_channel            (GimpColorSelector        *selector,
                                                            GimpColorSelectorChannel  channel);
static void       gimp_color_select_set_model_visible      (GimpColorSelector        *selector,
                                                            GimpColorSelectorModel    model,
                                                            gboolean                  visible);
static void       gimp_color_select_set_format             (GimpColorSelector        *selector,
                                                            const Babl               *format);
static void       gimp_color_select_set_simulation         (GimpColorSelector        *selector,
                                                            GimpColorProfile         *profile,
                                                            GimpColorRenderingIntent  intent,
                                                            gboolean                  bpc);
static void       gimp_color_select_set_config             (GimpColorSelector        *selector,
                                                            GimpColorConfig          *config);
static void       gimp_color_select_simulation             (GimpColorSelector        *selector,
                                                            gboolean                  enabled);

static void       gimp_color_select_channel_toggled        (GtkWidget                *widget,
                                                            GimpColorSelect          *select);

static void       gimp_color_select_update                 (GimpColorSelect          *select,
                                                            ColorSelectUpdateType     type);
static void       gimp_color_select_update_values          (GimpColorSelect          *select);
static void       gimp_color_select_update_pos             (GimpColorSelect          *select);

#if 0
static void       gimp_color_select_drop_color             (GtkWidget          *widget,
                                                            gint                x,
                                                            gint                y,
                                                            GeglColor          *color,
                                                            gpointer            data);
#endif

static void       gimp_color_select_xy_size_allocate       (GtkWidget          *widget,
                                                            GtkAllocation      *allocation,
                                                            GimpColorSelect    *select);
static gboolean   gimp_color_select_xy_draw                (GtkWidget          *widget,
                                                            cairo_t            *cr,
                                                            GimpColorSelect    *select);
static gboolean   gimp_color_select_xy_events              (GtkWidget          *widget,
                                                            GdkEvent           *event,
                                                            GimpColorSelect    *select);
static void       gimp_color_select_z_size_allocate        (GtkWidget          *widget,
                                                            GtkAllocation      *allocation,
                                                            GimpColorSelect    *select);
static gboolean   gimp_color_select_z_draw                 (GtkWidget          *widget,
                                                            cairo_t            *cr,
                                                            GimpColorSelect    *select);
static gboolean   gimp_color_select_z_events               (GtkWidget          *widget,
                                                            GdkEvent           *event,
                                                            GimpColorSelect    *select);

static void       gimp_color_select_render                 (GtkWidget          *widget,
                                                            guchar             *buf,
                                                            gint                width,
                                                            gint                height,
                                                            gint                rowstride,
                                                            ColorSelectFillType fill_type,
                                                            GeglColor          *color,
                                                            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_notify_config          (GimpColorConfig  *config,
                                                            const GParamSpec *pspec,
                                                            GimpColorSelect  *select);

static gfloat     gimp_color_select_get_z_value            (GimpColorSelect *select,
                                                            GeglColor       *color);

static void       gimp_color_select_set_label              (GimpColorSelect *select);


G_DEFINE_TYPE (GimpColorSelect, gimp_color_select, GIMP_TYPE_COLOR_SELECTOR)

#define parent_class gimp_color_select_parent_class

static const ColorSelectRenderFunc render_funcs[] =
{
  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_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_lch_to_rgb       = NULL;
static const Babl *fish_lch_to_rgb_u8    = NULL;
static const Babl *fish_lch_to_softproof = NULL;
static const Babl *rgbf_format           = NULL;
static const Babl *rgbu_format           = NULL;
static const Babl *hsvf_format           = NULL;
static const Babl *softproof_format      = 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;
  selector_class->set_toggles_visible   = gimp_color_select_togg_visible;
  selector_class->set_toggles_sensitive = gimp_color_select_togg_sensitive;
  selector_class->set_color             = gimp_color_select_set_color;
  selector_class->set_channel           = gimp_color_select_set_channel;
  selector_class->set_model_visible     = gimp_color_select_set_model_visible;
  selector_class->set_simulation        = gimp_color_select_set_simulation;
  selector_class->set_format            = gimp_color_select_set_format;
  selector_class->set_config            = gimp_color_select_set_config;
  selector_class->simulation            = gimp_color_select_simulation;

  gtk_widget_class_set_css_name (GTK_WIDGET_CLASS (klass), "GimpColorSelect");

  fish_lch_to_rgb    = babl_fish (babl_format ("CIE LCH(ab) float"),
                                  babl_format ("R'G'B' double"));
  fish_lch_to_rgb_u8 = babl_fish (babl_format ("CIE LCH(ab) float"),
                                  babl_format ("R'G'B' u8"));
  rgbf_format        = babl_format ("R'G'B' float");
  rgbu_format        = babl_format ("R'G'B' u8");
  hsvf_format        = babl_format ("HSV float");
}

static void
gimp_color_select_init (GimpColorSelect *select)
{
  GimpColorSelector      *selector = GIMP_COLOR_SELECTOR (select);
  GtkWidget              *hbox;
  GtkWidget              *frame;
  GtkWidget              *vbox;
  GtkWidget              *grid;
  GEnumClass             *model_class;
  GEnumClass             *channel_class;
  const GimpEnumDesc     *enum_desc;
  GimpColorSelectorModel  model;
  GSList                 *group = NULL;

  /* Default values. */
  select->z_value       = G_MINFLOAT;
  select->z_color_fill  = COLOR_SELECT_HUE;
  select->xy_color_fill = COLOR_SELECT_SATURATION_VALUE;
  select->drag_mode     = DRAG_NONE;

  hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 4);
  gtk_box_pack_start (GTK_BOX (select), hbox, TRUE, TRUE, 0);
  gtk_widget_show (hbox);

  /*  The x/y component preview  */
  frame = gtk_frame_new (NULL);
  gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
  gtk_box_pack_start (GTK_BOX (hbox), frame, TRUE, TRUE, 0);
  gtk_widget_show (frame);

  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,
                               GIMP_COLOR_SELECTOR_SIZE,
                               GIMP_COLOR_SELECTOR_SIZE);
  gtk_widget_set_events (select->xy_color, COLOR_AREA_EVENT_MASK);
  gtk_container_add (GTK_CONTAINER (frame), select->xy_color);
  gtk_widget_show (select->xy_color);

  g_signal_connect (select->xy_color, "size-allocate",
                    G_CALLBACK (gimp_color_select_xy_size_allocate),
                    select);
  g_signal_connect_after (select->xy_color, "draw",
                          G_CALLBACK (gimp_color_select_xy_draw),
                          select);
  g_signal_connect (select->xy_color, "event",
                    G_CALLBACK (gimp_color_select_xy_events),
                    select);

#if 0
  gimp_dnd_color_dest_add (select->xy_color, gimp_color_select_drop_color,
                           select);
#endif

  /*  The z component preview  */
  frame = gtk_frame_new (NULL);
  gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
  gtk_box_pack_start (GTK_BOX (hbox), frame, FALSE, FALSE, 0);
  gtk_widget_show (frame);

  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,
                               GIMP_COLOR_SELECTOR_BAR_SIZE, -1);
  gtk_widget_set_events (select->z_color, COLOR_AREA_EVENT_MASK);
  gtk_container_add (GTK_CONTAINER (frame), select->z_color);
  gtk_widget_show (select->z_color);

  g_signal_connect (select->z_color, "size-allocate",
                    G_CALLBACK (gimp_color_select_z_size_allocate),
                    select);
  g_signal_connect_after (select->z_color, "draw",
                          G_CALLBACK (gimp_color_select_z_draw),
                          select);
  g_signal_connect (select->z_color, "event",
                    G_CALLBACK (gimp_color_select_z_events),
                    select);

  vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
  gtk_box_pack_start (GTK_BOX (hbox), vbox, FALSE, FALSE, 0);
  gtk_widget_show (vbox);

  model_class   = g_type_class_ref (GIMP_TYPE_COLOR_SELECTOR_MODEL);
  channel_class = g_type_class_ref (GIMP_TYPE_COLOR_SELECTOR_CHANNEL);

  for (model = GIMP_COLOR_SELECTOR_MODEL_RGB;
       model <= GIMP_COLOR_SELECTOR_MODEL_HSV;
       model++)
    {
      enum_desc = gimp_enum_get_desc (model_class, model);

      select->toggle_box[model] = gtk_box_new (GTK_ORIENTATION_VERTICAL, 2);
      gtk_box_pack_start (GTK_BOX (vbox), select->toggle_box[model],
                          FALSE, FALSE, 0);

      if (gimp_color_selector_get_model_visible (selector, model))
        gtk_widget_show (select->toggle_box[model]);

      /*  channel toggles  */
      {
        GimpColorSelectorChannel  channel = GIMP_COLOR_SELECTOR_RED;
        GimpColorSelectorChannel  end_channel;

        switch (model)
          {
          case GIMP_COLOR_SELECTOR_MODEL_RGB:
            channel = GIMP_COLOR_SELECTOR_RED;
            break;
          case GIMP_COLOR_SELECTOR_MODEL_LCH:
            channel = GIMP_COLOR_SELECTOR_LCH_LIGHTNESS;
            break;
          case GIMP_COLOR_SELECTOR_MODEL_HSV:
            channel = GIMP_COLOR_SELECTOR_HUE;
            break;
          default:
            /* Should not happen. */
            g_return_if_reached ();
            break;
          }

        end_channel = channel + 3;

        for (; channel < end_channel; channel++)
          {
            GtkWidget *button;

            enum_desc = gimp_enum_get_desc (channel_class, channel);

            button = gtk_radio_button_new_with_mnemonic (group,
                                                         gettext (enum_desc->value_desc));
            group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (button));
            gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (button), FALSE);
            gtk_box_pack_start (GTK_BOX (select->toggle_box[model]), button,
                                TRUE, TRUE, 0);
            gtk_widget_show (button);

            g_object_set_data (G_OBJECT (button), "channel",
                               GINT_TO_POINTER (channel));

            if (channel == gimp_color_selector_get_channel (selector))
              gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);

            gimp_help_set_help_data (button, gettext (enum_desc->value_help),
                                     NULL);

            g_signal_connect (button, "toggled",
                              G_CALLBACK (gimp_color_select_channel_toggled),
                              select);
          }
      }
    }

  grid = gtk_grid_new ();
  gtk_grid_set_row_spacing (GTK_GRID (grid), 1);
  gtk_grid_set_column_spacing (GTK_GRID (grid), 1);
  gtk_box_pack_start (GTK_BOX (select), grid, FALSE, FALSE, 0);
  gtk_widget_set_visible (grid, TRUE);

  select->label = gtk_label_new (NULL);
  gtk_widget_set_halign (select->label, GTK_ALIGN_START);
  gtk_widget_set_vexpand (select->label, FALSE);
  gtk_label_set_ellipsize (GTK_LABEL (select->label), PANGO_ELLIPSIZE_END);
  gtk_label_set_justify (GTK_LABEL (select->label), GTK_JUSTIFY_LEFT);
  gtk_grid_attach (GTK_GRID (grid), select->label, 0, 0, 1, 1);
  gtk_widget_show (select->label);
  gimp_color_select_set_label (select);

  select->simulation_label = gtk_label_new (NULL);
  gtk_widget_set_halign (select->simulation_label, GTK_ALIGN_START);
  gtk_widget_set_vexpand (select->simulation_label, FALSE);
  gtk_label_set_ellipsize (GTK_LABEL (select->simulation_label), PANGO_ELLIPSIZE_END);
  gtk_label_set_justify (GTK_LABEL (select->simulation_label), GTK_JUSTIFY_LEFT);
  gtk_label_set_markup (GTK_LABEL (select->simulation_label), _("Soft-Proof Profile: <i>unknown</i>"));
  gtk_grid_attach (GTK_GRID (grid), select->simulation_label, 0, 1, 1, 1);

  g_type_class_unref (model_class);
  g_type_class_unref (channel_class);

  /* See z_color_fill and xy_color_fill values at top of the init()
   * function. Make sure the GimpColorSelector code is not out-of-sync.
   */

  gimp_color_selector_set_channel (selector, GIMP_COLOR_SELECTOR_HUE);
}

static void
gimp_color_select_finalize (GObject *object)
{
  GimpColorSelect *select = GIMP_COLOR_SELECT (object);

  g_clear_pointer (&select->xy_buf, g_free);
  select->xy_width     = 0;
  select->xy_height    = 0;
  select->xy_rowstride = 0;

  g_clear_pointer (&select->z_buf, g_free);
  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)
{
  GimpColorSelect *select = GIMP_COLOR_SELECT (selector);
  gint             i;

  for (i = 0; i < 3; i++)
    {
      gtk_widget_set_visible (select->toggle_box[i], visible);
    }
}

static void
gimp_color_select_togg_sensitive (GimpColorSelector *selector,
                                  gboolean           sensitive)
{
  GimpColorSelect *select = GIMP_COLOR_SELECT (selector);
  gint             i;

  for (i = 0; i < 3; i++)
    {
      gtk_widget_set_sensitive (select->toggle_box[i], sensitive);
    }
}

static void
gimp_color_select_set_color (GimpColorSelector *selector,
                             GeglColor         *color)
{
  GimpColorSelect       *select       = GIMP_COLOR_SELECT (selector);
  ColorSelectUpdateType  update_flags = UPDATE_POS;
  gfloat                 z_value;

  z_value = gimp_color_select_get_z_value (select, color);

/* Note: we are not comparing full colors, hence not using
 * gimp_color_is_perceptually_identical(). It's more of a quick comparison on
 * whether the Z channel is different enough that we'd want to redraw the XY
 * preview too.
 */

#define GIMP_RGBA_EPSILON 1e-4
  if (fabs (z_value - select->z_value) > GIMP_RGBA_EPSILON)
    update_flags |= UPDATE_XY_COLOR;
#undef GIMP_RGBA_EPSILON

  gimp_color_select_update (select, update_flags);
}

static void
gimp_color_select_set_channel (GimpColorSelector        *selector,
                               GimpColorSelectorChannel  channel)
{
  GimpColorSelect *select = GIMP_COLOR_SELECT (selector);

  switch ((ColorSelectFillType) channel)
    {
    case COLOR_SELECT_HUE:
      select->z_color_fill  = COLOR_SELECT_HUE;
      select->xy_color_fill = COLOR_SELECT_SATURATION_VALUE;
      break;

    case COLOR_SELECT_SATURATION:
      select->z_color_fill  = COLOR_SELECT_SATURATION;
      select->xy_color_fill = COLOR_SELECT_HUE_VALUE;
      break;

    case COLOR_SELECT_VALUE:
      select->z_color_fill  = COLOR_SELECT_VALUE;
      select->xy_color_fill = COLOR_SELECT_HUE_SATURATION;
      break;

    case COLOR_SELECT_RED:
      select->z_color_fill  = COLOR_SELECT_RED;
      select->xy_color_fill = COLOR_SELECT_GREEN_BLUE;
      break;

    case COLOR_SELECT_GREEN:
      select->z_color_fill  = COLOR_SELECT_GREEN;
      select->xy_color_fill = COLOR_SELECT_RED_BLUE;
      break;

    case COLOR_SELECT_BLUE:
      select->z_color_fill  = COLOR_SELECT_BLUE;
      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;
    }

  gimp_color_select_update (select,
                            UPDATE_POS | UPDATE_Z_COLOR | UPDATE_XY_COLOR);

  gimp_color_select_set_label (select);
}

static void
gimp_color_select_set_model_visible (GimpColorSelector      *selector,
                                     GimpColorSelectorModel  model,
                                     gboolean                visible)
{
  GimpColorSelect *select = GIMP_COLOR_SELECT (selector);

  if (! visible || gimp_color_selector_get_toggles_visible (selector))
    gtk_widget_set_visible (select->toggle_box[model], visible);
}

static void
gimp_color_select_set_format (GimpColorSelector *selector,
                              const Babl        *format)
{
  GimpColorSelect *select = GIMP_COLOR_SELECT (selector);

  if (select->format != format)
    {
      select->format = format;

      rgbf_format        = babl_format_with_space ("R'G'B' float", format);
      rgbu_format        = babl_format_with_space ("R'G'B' u8", format);
      hsvf_format        = babl_format_with_space ("HSV float", format);
      fish_lch_to_rgb    = babl_fish (babl_format ("CIE LCH(ab) float"),
                                      babl_format_with_space ("R'G'B' double", format));
      fish_lch_to_rgb_u8 = babl_fish (babl_format ("CIE LCH(ab) float"),
                                      babl_format_with_space ("R'G'B' u8", format));

      gimp_color_select_set_label (select);

      select->xy_needs_render = TRUE;
      select->z_needs_render  = TRUE;

      gtk_widget_queue_draw (select->xy_color);
      gtk_widget_queue_draw (select->z_color);
      gimp_color_select_update (select,
                                UPDATE_POS | UPDATE_XY_COLOR | UPDATE_Z_COLOR |
                                UPDATE_CALLER);
    }
}

static void
gimp_color_select_set_simulation (GimpColorSelector        *selector,
                                  GimpColorProfile         *profile,
                                  GimpColorRenderingIntent  intent,
                                  gboolean                  bpc)
{
  gimp_color_select_simulation (selector,
                                gimp_color_selector_get_simulation (selector,
                                                                    NULL, NULL, NULL));
}

static void
gimp_color_select_set_config (GimpColorSelector *selector,
                              GimpColorConfig   *config)
{
  GimpColorSelect *select = GIMP_COLOR_SELECT (selector);

  if (config != select->config)
    {
      if (select->config)
        {
          g_signal_handlers_disconnect_by_func (select->config,
                                                gimp_color_select_notify_config,
                                                select);
        }

      g_set_object (&select->config, config);

      if (select->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
gimp_color_select_simulation (GimpColorSelector *selector,
                              gboolean           enabled)
{
  GimpColorSelect  *select  = GIMP_COLOR_SELECT (selector);
  const Babl       *format  = NULL;
  GimpColorProfile *profile = NULL;

  if (enabled && gimp_color_selector_get_simulation (selector, &profile, NULL, NULL) && profile)
    {
      GError *error = NULL;

      if (gimp_color_profile_is_rgb (profile))
        format = gimp_color_profile_get_format (profile, babl_format ("R'G'B' float"),
                                                GIMP_COLOR_RENDERING_INTENT_RELATIVE_COLORIMETRIC,
                                                &error);
      else if (gimp_color_profile_is_cmyk (profile))
        format = gimp_color_profile_get_format (profile, babl_format ("CMYK float"),
                                                GIMP_COLOR_RENDERING_INTENT_RELATIVE_COLORIMETRIC,
                                                &error);
      else if (gimp_color_profile_is_gray (profile))
        format = gimp_color_profile_get_format (profile, babl_format ("Y' float"),
                                                GIMP_COLOR_RENDERING_INTENT_RELATIVE_COLORIMETRIC,
                                                &error);

      if (error)
        {
          g_printerr ("Color Selector: invalid color profile: %s\n", error->message);
          format = NULL;
          g_clear_error (&error);
        }
    }

  if (format)
    {
      softproof_format      = format;
      fish_lch_to_softproof = babl_fish (babl_format ("CIE LCH(ab) float"), format);

      if (babl_format_get_space (format) == babl_space ("sRGB"))
        {
          gtk_label_set_text (GTK_LABEL (select->simulation_label), _("Soft-Proof Profile: sRGB"));
          gimp_help_set_help_data (select->simulation_label, NULL, NULL);
        }
      else if (profile != NULL)
        {
          gchar *text;

          text = g_strdup_printf (_("Soft-Proof Profile: %s"), gimp_color_profile_get_label (profile));
          gtk_label_set_text (GTK_LABEL (select->simulation_label), text);
          gimp_help_set_help_data (select->simulation_label,
                                   gimp_color_profile_get_summary (profile),
                                   NULL);
          g_free (text);
        }
      else
        {
          gtk_label_set_markup (GTK_LABEL (select->simulation_label), _("Soft-Proof Profile: <i>unknown</i>"));
          gimp_help_set_help_data (select->simulation_label, NULL, NULL);
        }

      select->xy_needs_render = TRUE;
      select->z_needs_render  = TRUE;

      gtk_widget_queue_draw (select->xy_color);
      gtk_widget_queue_draw (select->z_color);

      gtk_widget_show (select->simulation_label);
    }
  else
    {
      softproof_format      = NULL;
      fish_lch_to_softproof = NULL;

      gtk_label_set_markup (GTK_LABEL (select->simulation_label), _("Soft-Proof Profile: <i>unknown</i>"));
      gimp_help_set_help_data (select->simulation_label, NULL, NULL);
      gtk_widget_hide (select->simulation_label);
    }
}

static void
gimp_color_select_channel_toggled (GtkWidget       *widget,
                                   GimpColorSelect *select)
{
  if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget)))
    {
      GimpColorSelector        *selector = GIMP_COLOR_SELECTOR (select);
      GimpColorSelectorChannel  channel;

      channel = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (widget),
                                                    "channel"));

      gimp_color_selector_set_channel (selector, channel);
    }
}

static void
gimp_color_select_update (GimpColorSelect       *select,
                          ColorSelectUpdateType  update)
{
  if (update & UPDATE_POS)
    gimp_color_select_update_pos (select);

  if (update & UPDATE_VALUES)
    gimp_color_select_update_values (select);

  if (update & UPDATE_XY_COLOR)
    {
      select->xy_needs_render = TRUE;
      gtk_widget_queue_draw (select->xy_color);
    }

  if (update & UPDATE_Z_COLOR)
    {
      select->z_needs_render = TRUE;
      gtk_widget_queue_draw (select->z_color);
    }
}

static void
gimp_color_select_update_values (GimpColorSelect *select)
{
  GimpColorSelector *selector = GIMP_COLOR_SELECTOR (select);
  GeglColor         *color    = gimp_color_selector_get_color (selector);
  gdouble            values[3];
  gfloat             values_float[3];
  const Babl        *rgb_format;
  const Babl        *hsv_format;

  rgb_format = babl_format_with_space ("R'G'B' double", select->format);
  hsv_format = babl_format_with_space ("HSV float", select->format);

  switch (select->z_color_fill)
    {
    case COLOR_SELECT_RED:
      values[0] = select->pos[2];
      values[1] = select->pos[0];
      values[2] = select->pos[1];
      gegl_color_set_pixel (color, rgb_format, values);
      break;
    case COLOR_SELECT_GREEN:
      values[0] = select->pos[0];
      values[1] = select->pos[2];
      values[2] = select->pos[1];
      gegl_color_set_pixel (color, rgb_format, values);
      break;
    case COLOR_SELECT_BLUE:
      gegl_color_set_pixel (color, rgb_format, select->pos);
      break;

    case COLOR_SELECT_HUE:
      values_float[0] = select->pos[2];
      values_float[1] = select->pos[0];
      values_float[2] = select->pos[1];
      gegl_color_set_pixel (color, hsv_format, values_float);
      break;
    case COLOR_SELECT_SATURATION:
      values_float[0] = select->pos[0];
      values_float[1] = select->pos[2];
      values_float[2] = select->pos[1];
      gegl_color_set_pixel (color, hsv_format, values_float);
      break;
    case COLOR_SELECT_VALUE:
      values_float[0] = select->pos[0];
      values_float[1] = select->pos[1];
      values_float[2] = select->pos[2];
      gegl_color_set_pixel (color, hsv_format, values_float);
      break;

    case COLOR_SELECT_LCH_LIGHTNESS:
      values_float[0] = select->pos[2] * 100.0;
      values_float[1] = select->pos[1] * 200.0;
      values_float[2] = select->pos[0] * 360.0;
      gegl_color_set_pixel (color, babl_format ("CIE LCH(ab) float"), values_float);
      break;
    case COLOR_SELECT_LCH_CHROMA:
      values_float[0] = select->pos[1] * 100.0;
      values_float[1] = select->pos[2] * 200.0;
      values_float[2] = select->pos[0] * 360.0;
      gegl_color_set_pixel (color, babl_format ("CIE LCH(ab) float"), values_float);
      break;
    case COLOR_SELECT_LCH_HUE:
      values_float[0] = select->pos[1] * 100.0;
      values_float[1] = select->pos[0] * 200.0;
      values_float[2] = select->pos[2] * 360.0;
      gegl_color_set_pixel (color, babl_format ("CIE LCH(ab) float"), values_float);
      break;

    default:
      break;
    }

  gimp_color_selector_set_color (selector, color);

  g_object_unref (color);
}

static void
gimp_color_select_update_pos (GimpColorSelect *select)
{
  GimpColorSelector *selector = GIMP_COLOR_SELECTOR (select);
  GeglColor         *color    = gimp_color_selector_get_color (selector);
  gdouble            rgb[3];
  gfloat             lch[3];
  gfloat             hsv[3];

  gegl_color_get_pixel (color, babl_format_with_space ("R'G'B' double", select->format), rgb);
  gegl_color_get_pixel (color, babl_format_with_space ("HSV float", select->format), hsv);
  gegl_color_get_pixel (color, babl_format ("CIE LCH(ab) float"), lch);
  g_object_unref (color);

  switch (select->z_color_fill)
    {
    case COLOR_SELECT_RED:
      select->pos[0] = CLAMP (rgb[1], 0.0, 1.0);
      select->pos[1] = CLAMP (rgb[2], 0.0, 1.0);
      select->pos[2] = CLAMP (rgb[0], 0.0, 1.0);
      break;
    case COLOR_SELECT_GREEN:
      select->pos[0] = CLAMP (rgb[0], 0.0, 1.0);
      select->pos[1] = CLAMP (rgb[2], 0.0, 1.0);
      select->pos[2] = CLAMP (rgb[1], 0.0, 1.0);
      break;
    case COLOR_SELECT_BLUE:
      select->pos[0] = CLAMP (rgb[0], 0.0, 1.0);
      select->pos[1] = CLAMP (rgb[1], 0.0, 1.0);
      select->pos[2] = CLAMP (rgb[2], 0.0, 1.0);
      break;

    case COLOR_SELECT_HUE:
      select->pos[0] = CLAMP (hsv[1], 0.0, 1.0);
      select->pos[1] = CLAMP (hsv[2], 0.0, 1.0);
      select->pos[2] = CLAMP (hsv[0], 0.0, 1.0);
      break;
    case COLOR_SELECT_SATURATION:
      select->pos[0] = CLAMP (hsv[0], 0.0, 1.0);
      select->pos[1] = CLAMP (hsv[2], 0.0, 1.0);
      select->pos[2] = CLAMP (hsv[1], 0.0, 1.0);
      break;
    case COLOR_SELECT_VALUE:
      select->pos[0] = CLAMP (hsv[0], 0.0, 1.0);
      select->pos[1] = CLAMP (hsv[1], 0.0, 1.0);
      select->pos[2] = CLAMP (hsv[2], 0.0, 1.0);
      break;

    case COLOR_SELECT_LCH_LIGHTNESS:
      select->pos[0] = CLAMP (lch[2] / 360, 0.0, 1.0);
      select->pos[1] = CLAMP (lch[1] / 200, 0.0, 1.0);
      select->pos[2] = CLAMP (lch[0] / 100, 0.0, 1.0);
      break;
    case COLOR_SELECT_LCH_CHROMA:
      select->pos[0] = CLAMP (lch[2] / 360, 0.0, 1.0);
      select->pos[1] = CLAMP (lch[0] / 100, 0.0, 1.0);
      select->pos[2] = CLAMP (lch[1] / 200, 0.0, 1.0);
      break;
    case COLOR_SELECT_LCH_HUE:
      select->pos[0] = CLAMP (lch[1] / 200, 0.0, 1.0);
      select->pos[1] = CLAMP (lch[0] / 100, 0.0, 1.0);
      select->pos[2] = CLAMP (lch[2] / 360, 0.0, 1.0);
      break;

    default:
      break;
    }
}

#if 0
static void
gimp_color_select_drop_color (GtkWidget     *widget,
                              gint           x,
                              gint           y,
                              GeglColor     *color,
                              gpointer       data)
{
  GimpColorSelect *select = GIMP_COLOR_SELECT (data);

  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 |
                            UPDATE_CALLER);
}
#endif

static void
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);
}

static gboolean
gimp_color_select_xy_draw (GtkWidget       *widget,
                           cairo_t         *cr,
                           GimpColorSelect *select)
{
  GtkAllocation  allocation;
  GdkPixbuf     *pixbuf;
  const Babl    *render_space;
  const Babl    *render_fish;
  guchar        *buf;
  guchar        *src;
  guchar        *dest;
  gint           x, y;

  if (! select->xy_buf)
    return FALSE;

  if (select->xy_needs_render)
    {
      GimpColorSelector *selector = GIMP_COLOR_SELECTOR (select);
      GeglColor         *color    = gimp_color_selector_get_color (selector);

      gimp_color_select_render (select->xy_color,
                                select->xy_buf,
                                select->xy_width,
                                select->xy_height,
                                select->xy_rowstride,
                                select->xy_color_fill,
                                color,
                                select->oog_color);
      select->z_value = gimp_color_select_get_z_value (select, color);
      select->xy_needs_render = FALSE;

      g_object_unref (color);
    }

  render_space = gimp_widget_get_render_space (widget, select->config);
  render_fish  = babl_fish (babl_format_with_space ("R'G'B' u8", select->format),
                            babl_format_with_space ("R'G'B' u8", render_space));

  buf  = g_new (guchar, select->xy_rowstride * select->xy_height);
  src  = select->xy_buf;
  dest = buf;
  for (gint i = 0; i < select->xy_height; i++)
    {
      babl_process (render_fish, src, 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);

  gtk_widget_get_allocation (select->xy_color, &allocation);

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

  cairo_move_to (cr, 0,                y + 0.5);
  cairo_line_to (cr, allocation.width, y + 0.5);

  cairo_move_to (cr, x + 0.5, 0);
  cairo_line_to (cr, x + 0.5, allocation.height);

  cairo_set_line_width (cr, 3.0);
  cairo_set_source_rgba (cr, 1.0, 1.0, 1.0, 0.6);
  cairo_stroke_preserve (cr);

  cairo_set_line_width (cr, 1.0);
  cairo_set_source_rgba (cr, 0.0, 0.0, 0.0, 0.8);
  cairo_stroke (cr);

  return TRUE;
}

static gboolean
gimp_color_select_xy_events (GtkWidget       *widget,
                             GdkEvent        *event,
                             GimpColorSelect *select)
{
  GtkAllocation allocation;
  gdouble       x, y;

  switch (event->type)
    {
    case GDK_BUTTON_PRESS:
      {
        GdkEventButton *bevent = (GdkEventButton *) event;

        if (select->drag_mode != DRAG_NONE || bevent->button != 1)
          return FALSE;

        x = bevent->x;
        y = bevent->y;

        gtk_grab_add (widget);
        select->drag_mode = DRAG_XY;
      }
      break;

    case GDK_BUTTON_RELEASE:
      {
        GdkEventButton *bevent = (GdkEventButton *) event;

        if (select->drag_mode != DRAG_XY || bevent->button != 1)
          return FALSE;

        x = bevent->x;
        y = bevent->y;

        gtk_grab_remove (widget);
        select->drag_mode = DRAG_NONE;
      }
      break;

    case GDK_MOTION_NOTIFY:
      {
        GdkEventMotion *mevent = (GdkEventMotion *) event;

        if (select->drag_mode != DRAG_XY)
          return FALSE;

        x = mevent->x;
        y = mevent->y;
      }
      break;

    default:
      return FALSE;
    }

  gtk_widget_get_allocation (select->xy_color, &allocation);

  if (allocation.width > 1 && allocation.height > 1)
    {
      select->pos[0] = x  / (allocation.width - 1);
      select->pos[1] = 1.0 - y / (allocation.height - 1);
    }

  select->pos[0] = CLAMP (select->pos[0], 0.0, 1.0);
  select->pos[1] = CLAMP (select->pos[1], 0.0, 1.0);

  gtk_widget_queue_draw (select->xy_color);

  gimp_color_select_update (select, UPDATE_VALUES | UPDATE_CALLER);

  /* Ask for more motion events in case the event was a hint */
  gdk_event_request_motions ((GdkEventMotion *) event);

  return TRUE;
}

static void
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);
}

static gboolean
gimp_color_select_z_draw (GtkWidget       *widget,
                          cairo_t         *cr,
                          GimpColorSelect *select)
{
  GtkAllocation  allocation;
  GdkPixbuf     *pixbuf;
  const Babl    *render_space;
  const Babl    *render_fish;
  guchar        *buf;
  guchar        *src;
  guchar        *dest;
  gint           y;

  if (! select->z_buf)
    return FALSE;

  if (select->z_needs_render)
    {
      GimpColorSelector *selector = GIMP_COLOR_SELECTOR (select);
      GeglColor         *color    = gimp_color_selector_get_color (selector);

      gimp_color_select_render (select->z_color,
                                select->z_buf,
                                select->z_width,
                                select->z_height,
                                select->z_rowstride,
                                select->z_color_fill,
                                color,
                                select->oog_color);
      select->z_needs_render = FALSE;

      g_object_unref (color);
    }

  gtk_widget_get_allocation (widget, &allocation);

  render_space = gimp_widget_get_render_space (widget, select->config);
  render_fish  = babl_fish (babl_format_with_space ("R'G'B' u8", select->format),
                            babl_format_with_space ("R'G'B' u8", render_space));

  buf  = g_new (guchar, select->z_rowstride * select->z_height);
  src  = select->z_buf;
  dest = buf;
  for (gint i = 0; i < select->z_height; i++)
    {
      babl_process (render_fish, src, 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);

  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);
  cairo_line_to (cr, allocation.width, y + 0.5);

  cairo_set_line_width (cr, 3.0);
  cairo_set_source_rgba (cr, 1.0, 1.0, 1.0, 0.6);
  cairo_stroke_preserve (cr);

  cairo_set_line_width (cr, 1.0);
  cairo_set_source_rgba (cr, 0.0, 0.0, 0.0, 0.8);
  cairo_stroke (cr);

  return TRUE;
}

static gboolean
gimp_color_select_z_events (GtkWidget       *widget,
                            GdkEvent        *event,
                            GimpColorSelect *select)
{
  GtkAllocation allocation;
  gdouble       z;

  switch (event->type)
    {
    case GDK_BUTTON_PRESS:
      {
        GdkEventButton *bevent = (GdkEventButton *) event;

        if (select->drag_mode != DRAG_NONE || bevent->button != 1)
          return FALSE;

        z = bevent->y;

        gtk_grab_add (widget);
        select->drag_mode = DRAG_Z;
      }
      break;

    case GDK_BUTTON_RELEASE:
      {
        GdkEventButton *bevent = (GdkEventButton *) event;

        if (select->drag_mode != DRAG_Z || bevent->button != 1)
          return FALSE;

        z = bevent->y;

        gtk_grab_remove (widget);
        select->drag_mode = DRAG_NONE;
      }
      break;

    case GDK_MOTION_NOTIFY:
      {
        GdkEventMotion *mevent = (GdkEventMotion *) event;

        if (select->drag_mode != DRAG_Z)
          return FALSE;

        z = mevent->y;
      }
      break;

    default:
      return FALSE;
    }

  gtk_widget_get_allocation (select->z_color, &allocation);

  if (allocation.height > 1)
    select->pos[2] = 1.0 - z / (allocation.height - 1);

  select->pos[2] = CLAMP (select->pos[2], 0.0, 1.0);

  gtk_widget_queue_draw (select->z_color);

  gimp_color_select_update (select,
                            UPDATE_VALUES | UPDATE_XY_COLOR | UPDATE_CALLER);

  /* Ask for more motion events in case the event was a hint */
  gdk_event_request_motions ((GdkEventMotion *) event);

  return TRUE;
}

static void
gimp_color_select_render (GtkWidget           *preview,
                          guchar              *buf,
                          gint                 width,
                          gint                 height,
                          gint                 rowstride,
                          ColorSelectFillType  fill_type,
                          GeglColor           *color,
                          const guchar        *oog_color)
{
  ColorSelectFill  csf;

  csf.width       = width;
  csf.height      = height;
  csf.render_line = render_funcs[fill_type];

  gegl_color_get_pixel (color, rgbf_format, csf.rgb);
  gegl_color_get_pixel (color, hsvf_format, csf.hsv);
  gegl_color_get_pixel (color, babl_format ("CIE LCH(ab) alpha float"), csf.lch);

  csf.oog_color[0] = oog_color[0];
  csf.oog_color[1] = oog_color[1];
  csf.oog_color[2] = oog_color[2];

  for (csf.y = 0; csf.y < csf.height; csf.y++)
    {
      csf.buffer = buf;

      csf.render_line (&csf);

      buf += rowstride;
    }
}

static void
color_select_render_red (ColorSelectFill *csf)
{
  guchar *p = csf->buffer;
  gint    i, r;

  r = (csf->height - csf->y + 1) * 255 / csf->height;
  r = CLAMP (r, 0, 255);

  for (i = 0; i < csf->width; i++)
    {
      *p++ = r;
      *p++ = 0;
      *p++ = 0;
    }
}

static void
color_select_render_green (ColorSelectFill *csf)
{
  guchar *p = csf->buffer;
  gint    i, g;

  g = (csf->height - csf->y + 1) * 255 / csf->height;
  g = CLAMP (g, 0, 255);

  for (i = 0; i < csf->width; i++)
    {
      *p++ = 0;
      *p++ = g;
      *p++ = 0;
    }
}

static void
color_select_render_blue (ColorSelectFill *csf)
{
  guchar *p = csf->buffer;
  gint    i, b;

  b = (csf->height - csf->y + 1) * 255 / csf->height;
  b = CLAMP (b, 0, 255);

  for (i = 0; i < csf->width; i++)
    {
      *p++ = 0;
      *p++ = 0;
      *p++ = b;
    }
}

static void
color_select_render_hue (ColorSelectFill *csf)
{
  guchar *p = csf->buffer;
  gfloat  h, f;
  gint    r, g, b;
  gint    i;

  h = csf->y * 360.0 / csf->height;
  h = CLAMP (360 - h, 0, 360);

  h /= 60;
  f = (h - (int) h) * 255;

  r = g = b = 0;

  switch ((int) h)
    {
    case 0:
      r = 255;
      g = f;
      b = 0;
      break;
    case 1:
      r = 255 - f;
      g = 255;
      b = 0;
      break;
    case 2:
      r = 0;
      g = 255;
      b = f;
      break;
    case 3:
      r = 0;
      g = 255 - f;
      b = 255;
      break;
    case 4:
      r = f;
      g = 0;
      b = 255;
      break;
    case 5:
      r = 255;
      g = 0;
      b = 255 - f;
      break;
    }

  for (i = 0; i < csf->width; i++)
    {
      *p++ = r;
      *p++ = g;
      *p++ = b;
    }
}

static void
color_select_render_saturation (ColorSelectFill *csf)
{
  guchar *p = csf->buffer;
  gint    s;
  gint    i;

  s = csf->y * 255 / csf->height;
  s = CLAMP (s, 0, 255);

  s = 255 - s;

  for (i = 0; i < csf->width; i++)
    {
      *p++ = s;
      *p++ = s;
      *p++ = s;
    }
}

static void
color_select_render_value (ColorSelectFill *csf)
{
  guchar *p = csf->buffer;
  gint    v;
  gint    i;

  v = csf->y * 255 / csf->height;
  v = CLAMP (v, 0, 255);

  v = 255 - v;

  for (i = 0; i < csf->width; i++)
    {
      *p++ = v;
      *p++ = v;
      *p++ = v;
    }
}

static void
color_select_render_lch_lightness (ColorSelectFill *csf)
{
  guchar  *p   = csf->buffer;
  gfloat   lch[4] = { 0.0, 0.0, 0.0, 1.0 };
  guchar   rgb[3];
  gint     i;

  lch[0] = (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;
  gfloat   lch[4] = { 80.0, 0.0, 0.0, 1.0 };
  guchar   rgb[3];
  gint     i;

  lch[1] = (csf->height - 1 - csf->y) * 200.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;
  gfloat   lch[4] = { 80.0, 200.0, 0.0, 1.0 };
  guchar   rgb[3];
  gint     i;

  lch[2] = (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)
{
  GeglColor *c = NULL;
  guchar    *p = csf->buffer;
  gfloat     dr;
  gfloat     rgb[3];
  gint       i;

  c = gegl_color_new (NULL);

  rgb[0] = 0.f;
  rgb[1] = ((gfloat) (csf->height - csf->y + 1)) / csf->height;
  rgb[2] = csf->rgb[2];

  dr = 1.f / csf->width;

  for (i = 0; i < csf->width; i++)
    {
      gegl_color_set_pixel (c, rgbf_format, rgb);
      if (softproof_format &&
          gimp_color_is_out_of_gamut (c, babl_format_get_space (softproof_format)))
        {
          *p++ = csf->oog_color[0];
          *p++ = csf->oog_color[1];
          *p++ = csf->oog_color[2];
        }
      else
        {
          gegl_color_get_pixel (c, rgbu_format, p);
          p += 3;
        }

      rgb[0] += dr;
    }

  g_object_unref (c);
}

static void
color_select_render_red_blue (ColorSelectFill *csf)
{
  GeglColor *c = NULL;
  guchar    *p = csf->buffer;
  gfloat     rgb[3];
  gfloat     dr;
  gint       i;

  c = gegl_color_new (NULL);

  rgb[0] = 0.f;
  rgb[1] = csf->rgb[1];
  rgb[2] = ((gfloat) (csf->height - csf->y + 1)) / csf->height;

  dr = 1.f / csf->width;

  for (i = 0; i < csf->width; i++)
    {
      gegl_color_set_pixel (c, rgbf_format, rgb);
      if (softproof_format &&
          gimp_color_is_out_of_gamut (c, babl_format_get_space (softproof_format)))
        {
          *p++ = csf->oog_color[0];
          *p++ = csf->oog_color[1];
          *p++ = csf->oog_color[2];
        }
      else
        {
          gegl_color_get_pixel (c, rgbu_format, p);
          p += 3;
        }

      rgb[0] += dr;
    }

  g_object_unref (c);
}

static void
color_select_render_green_blue (ColorSelectFill *csf)
{
  GeglColor *c = NULL;
  guchar    *p = csf->buffer;
  gfloat     rgb[3];
  gfloat     dg;
  gint       i;

  c = gegl_color_new (NULL);

  rgb[0] = csf->rgb[0];
  rgb[1] = 0.f;
  rgb[2] = ((gfloat) (csf->height - csf->y + 1)) / csf->height;

  dg = 1.f / csf->width;

  for (i = 0; i < csf->width; i++)
    {
      gegl_color_set_pixel (c, rgbf_format, rgb);
      if (softproof_format &&
          gimp_color_is_out_of_gamut (c, babl_format_get_space (softproof_format)))
        {
          *p++ = csf->oog_color[0];
          *p++ = csf->oog_color[1];
          *p++ = csf->oog_color[2];
        }
      else
        {
          gegl_color_get_pixel (c, rgbu_format, p);
          p += 3;
        }

      rgb[1] += dg;
    }

  g_object_unref (c);
}

static void
color_select_render_hue_saturation (ColorSelectFill *csf)
{
  GeglColor *c = NULL;
  guchar    *p = csf->buffer;
  gfloat     hsv[3];
  gfloat     dh;

  c = gegl_color_new (NULL);

  hsv[0] = 0.f;
  hsv[1] = (gfloat) csf->y / csf->height;
  hsv[1] = CLAMP (hsv[1], 0.f, 1.f);
  hsv[1] = 1.f - hsv[1];
  hsv[2] = csf->hsv[2];

  dh = 1.f / (gfloat) csf->width;

  for (gint i = 0; i < csf->width; i++)
    {
      gegl_color_set_pixel (c, hsvf_format, hsv);
      if (softproof_format &&
          gimp_color_is_out_of_gamut (c, babl_format_get_space (softproof_format)))
        {
          *p++ = csf->oog_color[0];
          *p++ = csf->oog_color[1];
          *p++ = csf->oog_color[2];
        }
      else
        {
          gegl_color_get_pixel (c, rgbu_format, p);
          p += 3;
        }

      hsv[0] += dh;
    }

  g_object_unref (c);
}

static void
color_select_render_hue_value (ColorSelectFill *csf)
{
  GeglColor *c = NULL;
  guchar    *p = csf->buffer;
  gfloat     hsv[3];
  gfloat     dh;

  c = gegl_color_new (NULL);

  hsv[0] = 0.f;
  hsv[1] = csf->hsv[1];
  hsv[2] = (gfloat) csf->y / csf->height;
  hsv[2] = CLAMP (hsv[2], 0.f, 1.f);
  hsv[2] = 1.f - hsv[2];

  dh = 1.f / (gfloat) csf->width;

  for (gint i = 0; i < csf->width; i++)
    {
      gegl_color_set_pixel (c, hsvf_format, hsv);
      if (softproof_format &&
          gimp_color_is_out_of_gamut (c, babl_format_get_space (softproof_format)))
        {
          *p++ = csf->oog_color[0];
          *p++ = csf->oog_color[1];
          *p++ = csf->oog_color[2];
        }
      else
        {
          gegl_color_get_pixel (c, rgbu_format, p);
          p += 3;
        }

      hsv[0] += dh;
    }

  g_object_unref (c);
}

static void
color_select_render_saturation_value (ColorSelectFill *csf)
{
  GeglColor *c = NULL;
  guchar    *p = csf->buffer;
  gfloat     hsv[3];
  gfloat     ds;

  c = gegl_color_new (NULL);

  hsv[0] = csf->hsv[0];
  hsv[1] = 0.f;
  hsv[2] = 1.f - CLAMP ((gfloat) csf->y / csf->height, 0.f, 1.f);

  ds = 1.f / (gfloat) csf->width;

  for (gint i = 0; i < csf->width; i++)
    {
      gegl_color_set_pixel (c, hsvf_format, hsv);
      if (softproof_format &&
          gimp_color_is_out_of_gamut (c, babl_format_get_space (softproof_format)))
        {
          *p++ = csf->oog_color[0];
          *p++ = csf->oog_color[1];
          *p++ = csf->oog_color[2];
        }
      else
        {
          gegl_color_get_pixel (c, rgbu_format, p);
          p += 3;
        }

      hsv[1] += ds;
    }

  g_object_unref (c);
}

static void
color_select_render_lch_chroma_lightness (ColorSelectFill *csf)
{
  GeglColor *c = NULL;
  guchar    *p = csf->buffer;
  gfloat     lch[3];

  c = gegl_color_new (NULL);

  lch[0] = (csf->height - 1 - csf->y) * 100.0 / csf->height;
  lch[2] = csf->lch[2];

  for (gint i = 0; i < csf->width; i++)
    {
      lch[1] = i * 200.0 / csf->width;

      gegl_color_set_pixel (c, babl_format ("CIE LCH(ab) float"), lch);

      if (gimp_color_is_out_of_gamut (c, babl_format_get_space (rgbf_format)) ||
          (softproof_format &&
           gimp_color_is_out_of_gamut (c, babl_format_get_space (softproof_format))))
        {
          p[0] = csf->oog_color[0];
          p[1] = csf->oog_color[1];
          p[2] = csf->oog_color[2];
        }
      else
        {
          gegl_color_get_pixel (c, rgbu_format, p);
        }

      p += 3;
    }

  g_object_unref (c);
}

static void
color_select_render_lch_hue_lightness (ColorSelectFill *csf)
{
  GeglColor *c = NULL;
  guchar    *p = csf->buffer;
  gfloat     lch[3];

  c = gegl_color_new (NULL);

  lch[0] = (csf->height - 1 - csf->y) * 100.0 / csf->height;
  lch[1] = csf->lch[1];

  for (gint i = 0; i < csf->width; i++)
    {
      lch[2] = i * 360.0 / csf->width;

      gegl_color_set_pixel (c, babl_format ("CIE LCH(ab) float"), lch);

      if (gimp_color_is_out_of_gamut (c, babl_format_get_space (rgbf_format)) ||
          (softproof_format &&
           gimp_color_is_out_of_gamut (c, babl_format_get_space (softproof_format))))
        {
          p[0] = csf->oog_color[0];
          p[1] = csf->oog_color[1];
          p[2] = csf->oog_color[2];
        }
      else
        {
          gegl_color_get_pixel (c, rgbu_format, p);
        }

      p += 3;
    }

  g_object_unref (c);
}

static void
color_select_render_lch_hue_chroma (ColorSelectFill *csf)
{
  GeglColor *c = NULL;
  guchar    *p = csf->buffer;
  gfloat     lch[3];
  gint       i;

  c = gegl_color_new (NULL);

  lch[0] = csf->lch[0];
  lch[1] = (csf->height - 1 - csf->y) * 200.0 / csf->height;

  for (i = 0; i < csf->width; i++)
    {
      lch[2] = i * 360.0 / csf->width;

      gegl_color_set_pixel (c, babl_format ("CIE LCH(ab) float"), lch);

      if (gimp_color_is_out_of_gamut (c, babl_format_get_space (rgbf_format)) ||
          (softproof_format &&
           gimp_color_is_out_of_gamut (c, babl_format_get_space (softproof_format))))
        {
          p[0] = csf->oog_color[0];
          p[1] = csf->oog_color[1];
          p[2] = csf->oog_color[2];
        }
      else
        {
          gegl_color_get_pixel (c, rgbu_format, p);
        }

      p += 3;
    }

  g_object_unref (c);
}

static void
gimp_color_select_notify_config (GimpColorConfig  *config,
                                 const GParamSpec *pspec,
                                 GimpColorSelect  *select)
{
  GeglColor *color;

  color = gimp_color_config_get_out_of_gamut_color (config);
  /* TODO: shouldn't this be color-managed too, using the target space into
   * consideration?
   */

  gegl_color_get_pixel (color, babl_format ("R'G'B' u8"), select->oog_color);
  select->xy_needs_render = TRUE;
  select->z_needs_render  = TRUE;

  g_object_unref (color);
}

static gfloat
gimp_color_select_get_z_value (GimpColorSelect *select,
                               GeglColor       *color)
{
  gint   z_channel = -1;
  gfloat channels[3];

  switch (select->z_color_fill)
    {
    case COLOR_SELECT_RED:
      z_channel = 0;
    case COLOR_SELECT_GREEN:
      z_channel = (z_channel > -1) ? z_channel : 1;
    case COLOR_SELECT_BLUE:
      z_channel = (z_channel > -1) ? z_channel : 2;
      gegl_color_get_pixel (color, rgbf_format, channels);
      break;
    case COLOR_SELECT_HUE:
      z_channel = 0;
    case COLOR_SELECT_SATURATION:
      z_channel = (z_channel > -1) ? z_channel : 1;
    case COLOR_SELECT_VALUE:
      z_channel = (z_channel > -1) ? z_channel : 2;
      gegl_color_get_pixel (color, hsvf_format, channels);
      break;
    case COLOR_SELECT_LCH_LIGHTNESS:
      z_channel = 0;
    case COLOR_SELECT_LCH_CHROMA:
      z_channel = (z_channel > -1) ? z_channel : 1;
    case COLOR_SELECT_LCH_HUE:
      z_channel = (z_channel > -1) ? z_channel : 2;
      gegl_color_get_pixel (color, babl_format ("CIE LCH(ab) float"), channels);
      break;
    default:
      g_return_val_if_reached (G_MINFLOAT);
    }

  return channels[z_channel];
}

static void
gimp_color_select_set_label (GimpColorSelect *select)
{
  GEnumClass               *enum_class;
  const GimpEnumDesc       *enum_desc;
  const gchar              *model_help;
  gchar                    *model_label;
  GimpColorSelectorChannel  channel;
  GimpColorSelectorModel    model      = GIMP_COLOR_SELECTOR_MODEL_RGB;
  gboolean                  with_space = TRUE;

  channel = gimp_color_selector_get_channel (GIMP_COLOR_SELECTOR (select));
  switch (channel)
    {
    case GIMP_COLOR_SELECTOR_HUE:
    case GIMP_COLOR_SELECTOR_SATURATION:
    case GIMP_COLOR_SELECTOR_VALUE:
      model = GIMP_COLOR_SELECTOR_MODEL_HSV;
      break;
    case GIMP_COLOR_SELECTOR_RED:
    case GIMP_COLOR_SELECTOR_GREEN:
    case GIMP_COLOR_SELECTOR_BLUE:
      model = GIMP_COLOR_SELECTOR_MODEL_RGB;
      break;
    case GIMP_COLOR_SELECTOR_LCH_LIGHTNESS:
    case GIMP_COLOR_SELECTOR_LCH_CHROMA:
    case GIMP_COLOR_SELECTOR_LCH_HUE:
      model = GIMP_COLOR_SELECTOR_MODEL_LCH;
      with_space = FALSE;
      break;
    case GIMP_COLOR_SELECTOR_ALPHA:
      /* Let's ignore label change when alpha is selected (it can be
       * used with any model).
       */

      return;
    }

  enum_class  = g_type_class_ref (GIMP_TYPE_COLOR_SELECTOR_MODEL);
  enum_desc   = gimp_enum_get_desc (enum_class, model);
  model_help  = gettext (enum_desc->value_help);
  model_label = g_strdup_printf (_("Model: %s"), gettext (enum_desc->value_desc));

  if (with_space)
    {
      if (select->format == NULL || babl_format_get_space (select->format) == babl_space ("sRGB"))
        {
          gchar *label;

          /* TODO: in future, this could be a better thought localized
           * string, but for now, I had to preserve string freeze.
           */

          label = g_strdup_printf ("%s - %s", model_label, _("Profile: sRGB"));
          gtk_label_set_text (GTK_LABEL (select->label), label);
          gimp_help_set_help_data (select->label, model_help, NULL);
          g_free (label);
        }
      else
        {
          GimpColorProfile *profile = NULL;
          const gchar      *icc;
          gint              icc_len;

          icc = babl_space_get_icc (babl_format_get_space (select->format), &icc_len);
          profile = gimp_color_profile_new_from_icc_profile ((const guint8 *) icc, icc_len, NULL);

          if (profile != NULL)
            {
              gchar *label;
              gchar *text;

              text  = g_strdup_printf (_("Profile: %s"), gimp_color_profile_get_label (profile));
              label = g_strdup_printf ("%s - %s", model_label, text);
              gtk_label_set_text (GTK_LABEL (select->label), label);
              gimp_help_set_help_data (select->label,
                                       gimp_color_profile_get_summary (profile),
                                       NULL);
              g_free (text);
              g_free (label);
            }
          else
            {
              gchar *label;

              label = g_strdup_printf ("%s - %s", model_label, _("Profile: <i>unknown</i>"));
              gtk_label_set_markup (GTK_LABEL (select->label), label);
              gimp_help_set_help_data (select->label, model_help, NULL);
              g_free (label);
            }

          g_clear_object (&profile);
        }
    }
  else
    {
      gtk_label_set_text (GTK_LABEL (select->label), model_label);
      gimp_help_set_help_data (select->label, model_help, NULL);
    }

  g_free (model_label);
}

Messung
CHZG
839610093

¤ Dauer der Verarbeitung: 0.20 Sekunden  (vorverarbeitet am  2026-11-04) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover


Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.