gimp/libgimpwidgets/gimpcolorscale.c

1145 lines
34 KiB
C

/* 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
* <http://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) \
G_TYPE_INSTANCE_GET_PRIVATE (obj, \
GIMP_TYPE_COLOR_SCALE, \
GimpColorScalePrivate)
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 (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));
g_type_class_add_private (object_class, sizeof (GimpColorScalePrivate));
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);
if (scale->buf)
{
g_free (scale->buf);
scale->buf = NULL;
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);
g_object_unref (priv->config);
gimp_color_scale_destroy_transform (scale);
}
priv->config = config;
if (priv->config)
{
g_object_ref (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;
}