diff options
Diffstat (limited to 'libgimpwidgets/gimpcolorscale.c')
-rw-r--r-- | libgimpwidgets/gimpcolorscale.c | 1133 |
1 files changed, 1133 insertions, 0 deletions
diff --git a/libgimpwidgets/gimpcolorscale.c b/libgimpwidgets/gimpcolorscale.c new file mode 100644 index 0000000..f87ab08 --- /dev/null +++ b/libgimpwidgets/gimpcolorscale.c @@ -0,0 +1,1133 @@ +/* LIBGIMP - The GIMP Library + * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball + * + * gimpcolorscale.c + * Copyright (C) 2002-2010 Sven Neumann <sven@gimp.org> + * Michael Natterer <mitch@gimp.org> + * + * 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 <string.h> + +#include <gegl.h> +#include <gtk/gtk.h> + +#include "libgimpbase/gimpbase.h" +#include "libgimpconfig/gimpconfig.h" +#include "libgimpcolor/gimpcolor.h" + +#include "gimpwidgetstypes.h" + +#include "gimpcairo-utils.h" +#include "gimpcolorscale.h" +#include "gimpwidgetsutils.h" + + +/** + * SECTION: gimpcolorscale + * @title: GimpColorScale + * @short_description: Fancy colored sliders. + * + * Fancy colored sliders. + **/ + + +enum +{ + PROP_0, + PROP_CHANNEL +}; + + +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) \ + ((GimpColorScalePrivate *) gimp_color_scale_get_instance_private ((GimpColorScale *) (obj))) + + +static void gimp_color_scale_dispose (GObject *object); +static void gimp_color_scale_finalize (GObject *object); +static void gimp_color_scale_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); +static void gimp_color_scale_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); + +static void gimp_color_scale_size_allocate (GtkWidget *widget, + GtkAllocation *allocation); +static void gimp_color_scale_state_changed (GtkWidget *widget, + GtkStateType previous_state); +static gboolean gimp_color_scale_button_press (GtkWidget *widget, + GdkEventButton *event); +static gboolean gimp_color_scale_button_release (GtkWidget *widget, + GdkEventButton *event); +static gboolean gimp_color_scale_scroll (GtkWidget *widget, + GdkEventScroll *event); +static gboolean gimp_color_scale_expose (GtkWidget *widget, + GdkEventExpose *event); + +static void gimp_color_scale_render (GimpColorScale *scale); +static void gimp_color_scale_render_alpha (GimpColorScale *scale); +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_WITH_PRIVATE (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) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); + + object_class->dispose = gimp_color_scale_dispose; + object_class->finalize = gimp_color_scale_finalize; + object_class->get_property = gimp_color_scale_get_property; + object_class->set_property = gimp_color_scale_set_property; + + widget_class->size_allocate = gimp_color_scale_size_allocate; + widget_class->state_changed = gimp_color_scale_state_changed; + widget_class->button_press_event = gimp_color_scale_button_press; + widget_class->button_release_event = gimp_color_scale_button_release; + widget_class->scroll_event = gimp_color_scale_scroll; + widget_class->expose_event = gimp_color_scale_expose; + + /** + * GimpColorScale:channel: + * + * The channel which is edited by the color scale. + * + * Since: 2.8 + */ + g_object_class_install_property (object_class, PROP_CHANNEL, + g_param_spec_enum ("channel", + "Channel", + "The channel which is edited by the color scale", + GIMP_TYPE_COLOR_SELECTOR_CHANNEL, + GIMP_COLOR_SELECTOR_VALUE, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + 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 +gimp_color_scale_dispose (GObject *object) +{ + GimpColorScale *scale = GIMP_COLOR_SCALE (object); + + gimp_color_scale_set_color_config (scale, NULL); + + G_OBJECT_CLASS (parent_class)->dispose (object); +} + +static void +gimp_color_scale_init (GimpColorScale *scale) +{ + GtkRange *range = GTK_RANGE (scale); + + gtk_range_set_slider_size_fixed (range, TRUE); + gtk_range_set_flippable (GTK_RANGE (scale), TRUE); + + gtk_scale_set_draw_value (GTK_SCALE (scale), FALSE); + + scale->channel = GIMP_COLOR_SELECTOR_VALUE; + scale->needs_render = TRUE; + + gtk_orientable_set_orientation (GTK_ORIENTABLE (range), + GTK_ORIENTATION_HORIZONTAL); + + gimp_rgba_set (&scale->rgb, 0.0, 0.0, 0.0, 1.0); + gimp_rgb_to_hsv (&scale->rgb, &scale->hsv); + + gimp_widget_track_monitor (GTK_WIDGET (scale), + G_CALLBACK (gimp_color_scale_destroy_transform), + NULL); +} + +static void +gimp_color_scale_finalize (GObject *object) +{ + GimpColorScale *scale = GIMP_COLOR_SCALE (object); + + g_clear_pointer (&scale->buf, g_free); + scale->width = 0; + scale->height = 0; + scale->rowstride = 0; + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static void +gimp_color_scale_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GimpColorScale *scale = GIMP_COLOR_SCALE (object); + + switch (property_id) + { + case PROP_CHANNEL: + g_value_set_enum (value, scale->channel); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_color_scale_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + GimpColorScale *scale = GIMP_COLOR_SCALE (object); + + switch (property_id) + { + case PROP_CHANNEL: + gimp_color_scale_set_channel (scale, g_value_get_enum (value)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_color_scale_size_allocate (GtkWidget *widget, + GtkAllocation *allocation) +{ + GimpColorScale *scale = GIMP_COLOR_SCALE (widget); + GtkRange *range = GTK_RANGE (widget); + GdkRectangle range_rect; + gint focus = 0; + gint trough_border; + gint scale_width; + gint scale_height; + + gtk_widget_style_get (widget, + "trough-border", &trough_border, + NULL); + + if (gtk_widget_get_can_focus (widget)) + { + gint focus_padding = 0; + + gtk_widget_style_get (widget, + "focus-line-width", &focus, + "focus-padding", &focus_padding, + NULL); + focus += focus_padding; + } + + gtk_range_set_min_slider_size (range, + (MIN (allocation->width, + allocation->height) - 2 * focus) / 2); + + if (GTK_WIDGET_CLASS (parent_class)->size_allocate) + GTK_WIDGET_CLASS (parent_class)->size_allocate (widget, allocation); + + gtk_range_get_range_rect (range, &range_rect); + + scale_width = range_rect.width - 2 * (focus + trough_border); + scale_height = range_rect.height - 2 * (focus + trough_border); + + switch (gtk_orientable_get_orientation (GTK_ORIENTABLE (range))) + { + case GTK_ORIENTATION_HORIZONTAL: + scale_width -= gtk_range_get_min_slider_size (range) - 1; + scale_height -= 2; + break; + + case GTK_ORIENTATION_VERTICAL: + scale_width -= 2; + scale_height -= gtk_range_get_min_slider_size (range) - 1; + break; + } + + if (scale_width != scale->width || scale_height != scale->height) + { + scale->width = scale_width; + scale->height = scale_height; + + scale->rowstride = scale->width * 4; + + g_free (scale->buf); + scale->buf = g_new (guchar, scale->rowstride * scale->height); + + scale->needs_render = TRUE; + } +} + +static void +gimp_color_scale_state_changed (GtkWidget *widget, + GtkStateType previous_state) +{ + if (gtk_widget_get_state (widget) == GTK_STATE_INSENSITIVE || + previous_state == GTK_STATE_INSENSITIVE) + { + GIMP_COLOR_SCALE (widget)->needs_render = TRUE; + } + + if (GTK_WIDGET_CLASS (parent_class)->state_changed) + GTK_WIDGET_CLASS (parent_class)->state_changed (widget, previous_state); +} + +static gboolean +gimp_color_scale_button_press (GtkWidget *widget, + GdkEventButton *event) +{ + if (event->button == 1) + { + GdkEventButton *my_event; + gboolean retval; + + my_event = (GdkEventButton *) gdk_event_copy ((GdkEvent *) event); + my_event->button = 2; + + retval = GTK_WIDGET_CLASS (parent_class)->button_press_event (widget, + my_event); + + gdk_event_free ((GdkEvent *) my_event); + + return retval; + } + + return GTK_WIDGET_CLASS (parent_class)->button_press_event (widget, event); +} + +static gboolean +gimp_color_scale_button_release (GtkWidget *widget, + GdkEventButton *event) +{ + if (event->button == 1) + { + GdkEventButton *my_event; + gboolean retval; + + my_event = (GdkEventButton *) gdk_event_copy ((GdkEvent *) event); + my_event->button = 2; + + retval = GTK_WIDGET_CLASS (parent_class)->button_release_event (widget, + my_event); + + gdk_event_free ((GdkEvent *) my_event); + + return retval; + } + + return GTK_WIDGET_CLASS (parent_class)->button_release_event (widget, event); +} + +static gboolean +gimp_color_scale_scroll (GtkWidget *widget, + GdkEventScroll *event) +{ + if (gtk_orientable_get_orientation (GTK_ORIENTABLE (widget)) == + GTK_ORIENTATION_HORIZONTAL) + { + GdkEventScroll *my_event; + gboolean retval; + + my_event = (GdkEventScroll *) gdk_event_copy ((GdkEvent *) event); + + switch (my_event->direction) + { + case GDK_SCROLL_UP: + my_event->direction = GDK_SCROLL_RIGHT; + break; + + case GDK_SCROLL_DOWN: + my_event->direction = GDK_SCROLL_LEFT; + break; + + default: + break; + } + + retval = GTK_WIDGET_CLASS (parent_class)->scroll_event (widget, my_event); + + gdk_event_free ((GdkEvent *) my_event); + + return retval; + } + + return GTK_WIDGET_CLASS (parent_class)->scroll_event (widget, event); +} + +static gboolean +gimp_color_scale_expose (GtkWidget *widget, + GdkEventExpose *event) +{ + GimpColorScale *scale = GIMP_COLOR_SCALE (widget); + GimpColorScalePrivate *priv = GET_PRIVATE (widget); + GtkRange *range = GTK_RANGE (widget); + GtkStyle *style = gtk_widget_get_style (widget); + GdkWindow *window = gtk_widget_get_window (widget); + gboolean sensitive = gtk_widget_is_sensitive (widget); + GtkAllocation allocation; + GdkRectangle range_rect; + GdkRectangle area = { 0, }; + cairo_surface_t *buffer; + gint focus = 0; + gint trough_border; + gint slider_start; + gint slider_size; + gint x, y; + gint w, h; + cairo_t *cr; + + if (! scale->buf || ! gtk_widget_is_drawable (widget)) + return FALSE; + + gtk_widget_get_allocation (widget, &allocation); + + cr = gdk_cairo_create (window); + gdk_cairo_region (cr, event->region); + cairo_translate (cr, allocation.x, allocation.y); + cairo_clip (cr); + + gtk_widget_style_get (widget, + "trough-border", &trough_border, + NULL); + + if (gtk_widget_get_can_focus (widget)) + { + gint focus_padding = 0; + + gtk_widget_style_get (widget, + "focus-line-width", &focus, + "focus-padding", &focus_padding, + NULL); + focus += focus_padding; + } + + gtk_range_get_range_rect (range, &range_rect); + gtk_range_get_slider_range (range, &slider_start, NULL); + + x = range_rect.x + focus; + y = range_rect.y + focus; + w = range_rect.width - 2 * focus; + h = range_rect.height - 2 * focus; + + slider_size = gtk_range_get_min_slider_size (range) / 2; + + if (scale->needs_render) + { + gimp_color_scale_render (scale); + + if (! sensitive) + gimp_color_scale_render_stipple (scale); + + scale->needs_render = FALSE; + } + + gtk_paint_box (style, window, + sensitive ? GTK_STATE_ACTIVE : GTK_STATE_INSENSITIVE, + GTK_SHADOW_IN, + &event->area, widget, "trough", + x + allocation.x, + y + allocation.y, + w, h); + + if (! priv->transform) + gimp_color_scale_create_transform (scale); + + if (priv->transform) + { + const Babl *format = babl_format ("cairo-RGB24"); + guchar *buf = g_new (guchar, scale->rowstride * scale->height); + guchar *src = scale->buf; + guchar *dest = buf; + gint i; + + for (i = 0; i < scale->height; i++) + { + gimp_color_transform_process_pixels (priv->transform, + format, src, + format, dest, + scale->width); + + src += scale->rowstride; + dest += scale->rowstride; + } + + buffer = cairo_image_surface_create_for_data (buf, + CAIRO_FORMAT_RGB24, + scale->width, + scale->height, + scale->rowstride); + cairo_surface_set_user_data (buffer, NULL, + buf, (cairo_destroy_func_t) g_free); + } + else + { + buffer = cairo_image_surface_create_for_data (scale->buf, + CAIRO_FORMAT_RGB24, + scale->width, + scale->height, + scale->rowstride); + } + + switch (gtk_orientable_get_orientation (GTK_ORIENTABLE (range))) + { + case GTK_ORIENTATION_HORIZONTAL: + cairo_set_source_surface (cr, buffer, + x + trough_border + slider_size, + y + trough_border + 1); + break; + + case GTK_ORIENTATION_VERTICAL: + cairo_set_source_surface (cr, buffer, + x + trough_border + 1, + y + trough_border + slider_size); + break; + } + + cairo_surface_destroy (buffer); + cairo_paint (cr); + + if (gtk_widget_has_focus (widget)) + gtk_paint_focus (style, window, gtk_widget_get_state (widget), + &event->area, widget, "trough", + range_rect.x + allocation.x, + range_rect.y + allocation.y, + range_rect.width, + range_rect.height); + + switch (gtk_orientable_get_orientation (GTK_ORIENTABLE (range))) + { + case GTK_ORIENTATION_HORIZONTAL: + area.x = slider_start; + area.y = y + trough_border; + area.width = 2 * slider_size + 1; + area.height = h - 2 * trough_border; + break; + + case GTK_ORIENTATION_VERTICAL: + area.x = x + trough_border; + area.y = slider_start; + area.width = w - 2 * trough_border; + area.height = 2 * slider_size + 1; + break; + } + + if (gtk_widget_is_sensitive (widget)) + gdk_cairo_set_source_color (cr, &style->black); + else + gdk_cairo_set_source_color (cr, &style->dark[GTK_STATE_INSENSITIVE]); + + switch (gtk_orientable_get_orientation (GTK_ORIENTABLE (range))) + { + case GTK_ORIENTATION_HORIZONTAL: + cairo_move_to (cr, area.x, area.y); + cairo_line_to (cr, area.x + area.width, area.y); + cairo_line_to (cr, + area.x + area.width / 2 + 0.5, + area.y + area.width / 2); + break; + + case GTK_ORIENTATION_VERTICAL: + cairo_move_to (cr, area.x, area.y); + cairo_line_to (cr, area.x, area.y + area.height); + cairo_line_to (cr, + area.x + area.height / 2, + area.y + area.height / 2 + 0.5); + break; + } + + cairo_close_path (cr); + cairo_fill (cr); + + if (gtk_widget_is_sensitive (widget)) + gdk_cairo_set_source_color (cr, &style->white); + else + gdk_cairo_set_source_color (cr, &style->light[GTK_STATE_INSENSITIVE]); + + switch (gtk_orientable_get_orientation (GTK_ORIENTABLE (range))) + { + case GTK_ORIENTATION_HORIZONTAL: + cairo_move_to (cr, area.x, area.y + area.height); + cairo_line_to (cr, area.x + area.width, area.y + area.height); + cairo_line_to (cr, + area.x + area.width / 2 + 0.5, + area.y + area.height - area.width / 2); + break; + + case GTK_ORIENTATION_VERTICAL: + cairo_move_to (cr, area.x + area.width, area.y); + cairo_line_to (cr, area.x + area.width, area.y + area.height); + cairo_line_to (cr, + area.x + area.width - area.height / 2, + area.y + area.height / 2 + 0.5); + break; + } + + cairo_close_path (cr); + cairo_fill (cr); + + cairo_destroy (cr); + + return FALSE; +} + +/** + * gimp_color_scale_new: + * @orientation: the scale's orientation (horizontal or vertical) + * @channel: the scale's color channel + * + * Creates a new #GimpColorScale widget. + * + * Return value: a new #GimpColorScale widget + **/ +GtkWidget * +gimp_color_scale_new (GtkOrientation orientation, + GimpColorSelectorChannel channel) +{ + GimpColorScale *scale = g_object_new (GIMP_TYPE_COLOR_SCALE, + "orientation", orientation, + "channel", channel, + NULL); + + gtk_range_set_flippable (GTK_RANGE (scale), + orientation == GTK_ORIENTATION_HORIZONTAL); + + return GTK_WIDGET (scale); +} + +/** + * gimp_color_scale_set_channel: + * @scale: a #GimpColorScale widget + * @channel: the new color channel + * + * Changes the color channel displayed by the @scale. + **/ +void +gimp_color_scale_set_channel (GimpColorScale *scale, + GimpColorSelectorChannel channel) +{ + g_return_if_fail (GIMP_IS_COLOR_SCALE (scale)); + + if (channel != scale->channel) + { + scale->channel = channel; + + scale->needs_render = TRUE; + gtk_widget_queue_draw (GTK_WIDGET (scale)); + + g_object_notify (G_OBJECT (scale), "channel"); + } +} + +/** + * gimp_color_scale_set_color: + * @scale: a #GimpColorScale widget + * @rgb: the new color as #GimpRGB + * @hsv: the new color as #GimpHSV + * + * Changes the color value of the @scale. + **/ +void +gimp_color_scale_set_color (GimpColorScale *scale, + const GimpRGB *rgb, + const GimpHSV *hsv) +{ + g_return_if_fail (GIMP_IS_COLOR_SCALE (scale)); + g_return_if_fail (rgb != NULL); + g_return_if_fail (hsv != NULL); + + scale->rgb = *rgb; + scale->hsv = *hsv; + + scale->needs_render = TRUE; + gtk_widget_queue_draw (GTK_WIDGET (scale)); +} + +/** + * gimp_color_scale_set_color_config: + * @scale: a #GimpColorScale widget. + * @config: a #GimpColorConfig object. + * + * Sets the color management configuration to use with this color scale. + * + * Since: 2.10 + */ +void +gimp_color_scale_set_color_config (GimpColorScale *scale, + GimpColorConfig *config) +{ + GimpColorScalePrivate *priv; + + g_return_if_fail (GIMP_IS_COLOR_SCALE (scale)); + g_return_if_fail (config == NULL || GIMP_IS_COLOR_CONFIG (config)); + + priv = GET_PRIVATE (scale); + + if (config != priv->config) + { + if (priv->config) + { + g_signal_handlers_disconnect_by_func (priv->config, + gimp_color_scale_notify_config, + scale); + + gimp_color_scale_destroy_transform (scale); + } + + g_set_object (&priv->config, config); + + if (priv->config) + { + g_signal_connect (priv->config, "notify", + G_CALLBACK (gimp_color_scale_notify_config), + scale); + + gimp_color_scale_notify_config (priv->config, NULL, scale); + } + } +} + + +/* as in gtkrange.c */ +static gboolean +should_invert (GtkRange *range) +{ + gboolean inverted = gtk_range_get_inverted (range); + gboolean flippable = gtk_range_get_flippable (range); + + if (gtk_orientable_get_orientation (GTK_ORIENTABLE (range)) == + GTK_ORIENTATION_HORIZONTAL) + { + return + (inverted && !flippable) || + (inverted && flippable && + gtk_widget_get_direction (GTK_WIDGET (range)) == GTK_TEXT_DIR_LTR) || + (!inverted && flippable && + gtk_widget_get_direction (GTK_WIDGET (range)) == GTK_TEXT_DIR_RTL); + } + else + { + return inverted; + } +} + +static void +gimp_color_scale_render (GimpColorScale *scale) +{ + 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; + + if (scale->channel == GIMP_COLOR_SELECTOR_ALPHA) + { + gimp_color_scale_render_alpha (scale); + return; + } + + 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) + { + case GIMP_COLOR_SELECTOR_HUE: + case GIMP_COLOR_SELECTOR_SATURATION: + case GIMP_COLOR_SELECTOR_VALUE: + from_hsv = TRUE; + break; + + case GIMP_COLOR_SELECTOR_LCH_LIGHTNESS: + multiplier = 100; + from_lch = TRUE; + break; + case GIMP_COLOR_SELECTOR_LCH_CHROMA: + multiplier = 200; + from_lch = TRUE; + break; + case GIMP_COLOR_SELECTOR_LCH_HUE: + multiplier = 360; + from_lch = TRUE; + break; + + default: + break; + } + + invert = should_invert (range); + + switch (gtk_orientable_get_orientation (GTK_ORIENTABLE (range))) + { + case GTK_ORIENTATION_HORIZONTAL: + for (x = 0, d = buf; x < scale->width; x++, d += 4) + { + gdouble value = (gdouble) x * multiplier / (gdouble) (scale->width - 1); + guchar r, g, b; + + if (invert) + value = multiplier - value; + + *channel_value = value; + + if (from_hsv) + gimp_hsv_to_rgb (&hsv, &rgb); + else if (from_lch) + 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) + { + 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); + } + + d = buf + scale->rowstride; + for (y = 1; y < scale->height; y++) + { + memcpy (d, buf, scale->rowstride); + d += scale->rowstride; + } + break; + + case GTK_ORIENTATION_VERTICAL: + for (y = 0; y < scale->height; y++) + { + gdouble value = (gdouble) y * multiplier / (gdouble) (scale->height - 1); + guchar r, g, b; + + if (invert) + value = multiplier - value; + + *channel_value = value; + + if (from_hsv) + gimp_hsv_to_rgb (&hsv, &rgb); + else if (from_lch) + 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) + { + 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) + { + GIMP_CAIRO_RGB24_SET_PIXEL (d, r, g, b); + } + + buf += scale->rowstride; + } + break; + } +} + +static void +gimp_color_scale_render_alpha (GimpColorScale *scale) +{ + GtkRange *range = GTK_RANGE (scale); + GimpRGB rgb; + gboolean invert; + gdouble a; + guint x, y; + guchar *buf; + guchar *d, *l; + + invert = should_invert (range); + + buf = scale->buf; + rgb = scale->rgb; + + switch (gtk_orientable_get_orientation (GTK_ORIENTABLE (range))) + { + case GTK_ORIENTATION_HORIZONTAL: + { + guchar *light; + guchar *dark; + + light = buf; + /* this won't work correctly for very thin scales */ + dark = (scale->height > GIMP_CHECK_SIZE_SM ? + buf + GIMP_CHECK_SIZE_SM * scale->rowstride : light); + + for (x = 0, d = light, l = dark; x < scale->width; x++) + { + if ((x % GIMP_CHECK_SIZE_SM) == 0) + { + guchar *t; + + t = d; + d = l; + l = t; + } + + a = (gdouble) x / (gdouble) (scale->width - 1); + + if (invert) + a = 1.0 - a; + + GIMP_CAIRO_RGB24_SET_PIXEL (l, + (GIMP_CHECK_LIGHT + + (rgb.r - GIMP_CHECK_LIGHT) * a) * 255.999, + (GIMP_CHECK_LIGHT + + (rgb.g - GIMP_CHECK_LIGHT) * a) * 255.999, + (GIMP_CHECK_LIGHT + + (rgb.b - GIMP_CHECK_LIGHT) * a) * 255.999); + l += 4; + + GIMP_CAIRO_RGB24_SET_PIXEL (d, + (GIMP_CHECK_DARK + + (rgb.r - GIMP_CHECK_DARK) * a) * 255.999, + (GIMP_CHECK_DARK + + (rgb.g - GIMP_CHECK_DARK) * a) * 255.999, + (GIMP_CHECK_DARK + + (rgb.b - GIMP_CHECK_DARK) * a) * 255.999); + d += 4; + } + + for (y = 0, d = buf; y < scale->height; y++, d += scale->rowstride) + { + if (y == 0 || y == GIMP_CHECK_SIZE_SM) + continue; + + if ((y / GIMP_CHECK_SIZE_SM) & 1) + memcpy (d, dark, scale->rowstride); + else + memcpy (d, light, scale->rowstride); + } + } + break; + + case GTK_ORIENTATION_VERTICAL: + { + guchar light[4] = {0xff, 0xff, 0xff, 0xff}; + guchar dark[4] = {0xff, 0xff, 0xff, 0xff}; + + for (y = 0, d = buf; y < scale->height; y++, d += scale->rowstride) + { + a = (gdouble) y / (gdouble) (scale->height - 1); + + if (invert) + a = 1.0 - a; + + GIMP_CAIRO_RGB24_SET_PIXEL (light, + (GIMP_CHECK_LIGHT + + (rgb.r - GIMP_CHECK_LIGHT) * a) * 255.999, + (GIMP_CHECK_LIGHT + + (rgb.g - GIMP_CHECK_LIGHT) * a) * 255.999, + (GIMP_CHECK_LIGHT + + (rgb.b - GIMP_CHECK_LIGHT) * a) * 255.999); + + GIMP_CAIRO_RGB24_SET_PIXEL (dark, + (GIMP_CHECK_DARK + + (rgb.r - GIMP_CHECK_DARK) * a) * 255.999, + (GIMP_CHECK_DARK + + (rgb.g - GIMP_CHECK_DARK) * a) * 255.999, + (GIMP_CHECK_DARK + + (rgb.b - GIMP_CHECK_DARK) * a) * 255.999); + + for (x = 0, l = d; x < scale->width; x++, l += 4) + { + if (((x / GIMP_CHECK_SIZE_SM) ^ (y / GIMP_CHECK_SIZE_SM)) & 1) + { + l[0] = light[0]; + l[1] = light[1]; + l[2] = light[2]; + l[3] = light[3]; + } + else + { + l[0] = dark[0]; + l[1] = dark[1]; + l[2] = dark[2]; + l[3] = dark[3]; + } + } + } + } + break; + } +} + +/* + * This could be integrated into the render functions which might be + * slightly faster. But we trade speed for keeping the code simple. + */ +static void +gimp_color_scale_render_stipple (GimpColorScale *scale) +{ + GtkWidget *widget = GTK_WIDGET (scale); + GtkStyle *style = gtk_widget_get_style (widget); + guchar *buf; + guchar insensitive[4] = {0xff, 0xff, 0xff, 0xff}; + guint x, y; + + if ((buf = scale->buf) == NULL) + return; + + GIMP_CAIRO_RGB24_SET_PIXEL (insensitive, + style->bg[GTK_STATE_INSENSITIVE].red >> 8, + style->bg[GTK_STATE_INSENSITIVE].green >> 8, + style->bg[GTK_STATE_INSENSITIVE].blue >> 8); + + for (y = 0; y < scale->height; y++, buf += scale->rowstride) + { + guchar *d = buf + 4 * (y % 2); + + for (x = 0; x < scale->width - (y % 2); x += 2, d += 8) + { + d[0] = insensitive[0]; + d[1] = insensitive[1]; + d[2] = insensitive[2]; + d[3] = insensitive[3]; + } + } +} + +static void +gimp_color_scale_create_transform (GimpColorScale *scale) +{ + GimpColorScalePrivate *priv = GET_PRIVATE (scale); + + if (priv->config) + { + static GimpColorProfile *profile = NULL; + + const Babl *format = babl_format ("cairo-RGB24"); + + if (G_UNLIKELY (! profile)) + profile = gimp_color_profile_new_rgb_srgb (); + + priv->transform = gimp_widget_get_color_transform (GTK_WIDGET (scale), + priv->config, + profile, + format, + format); + } +} + +static void +gimp_color_scale_destroy_transform (GimpColorScale *scale) +{ + GimpColorScalePrivate *priv = GET_PRIVATE (scale); + + if (priv->transform) + { + g_object_unref (priv->transform); + priv->transform = NULL; + } + + 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; +} |