983 lines
30 KiB
C
983 lines
30 KiB
C
/* LIBPIKA - The PIKA Library
|
|
* Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
|
|
*
|
|
* pikacolorscale.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 "libpikabase/pikabase.h"
|
|
#include "libpikaconfig/pikaconfig.h"
|
|
#include "libpikacolor/pikacolor.h"
|
|
|
|
#include "pikawidgetstypes.h"
|
|
|
|
#include "pikacairo-utils.h"
|
|
#include "pikacolorscale.h"
|
|
#include "pikawidgetsutils.h"
|
|
|
|
|
|
/**
|
|
* SECTION: pikacolorscale
|
|
* @title: PikaColorScale
|
|
* @short_description: Fancy colored sliders.
|
|
*
|
|
* Fancy colored sliders.
|
|
**/
|
|
|
|
|
|
enum
|
|
{
|
|
PROP_0,
|
|
PROP_CHANNEL
|
|
};
|
|
|
|
|
|
typedef struct _PikaLCH PikaLCH;
|
|
|
|
struct _PikaLCH
|
|
{
|
|
gdouble l, c, h, a;
|
|
};
|
|
|
|
|
|
struct _PikaColorScalePrivate
|
|
{
|
|
PikaColorConfig *config;
|
|
PikaColorTransform *transform;
|
|
guchar oog_color[3];
|
|
|
|
PikaColorSelectorChannel channel;
|
|
PikaRGB rgb;
|
|
PikaHSV hsv;
|
|
|
|
guchar *buf;
|
|
guint width;
|
|
guint height;
|
|
guint rowstride;
|
|
|
|
gboolean needs_render;
|
|
};
|
|
|
|
#define GET_PRIVATE(obj) (((PikaColorScale *) (obj))->priv)
|
|
|
|
|
|
static void pika_color_scale_dispose (GObject *object);
|
|
static void pika_color_scale_finalize (GObject *object);
|
|
static void pika_color_scale_get_property (GObject *object,
|
|
guint property_id,
|
|
GValue *value,
|
|
GParamSpec *pspec);
|
|
static void pika_color_scale_set_property (GObject *object,
|
|
guint property_id,
|
|
const GValue *value,
|
|
GParamSpec *pspec);
|
|
|
|
static void pika_color_scale_size_allocate (GtkWidget *widget,
|
|
GtkAllocation *allocation);
|
|
static gboolean pika_color_scale_draw (GtkWidget *widget,
|
|
cairo_t *cr);
|
|
|
|
static void pika_color_scale_render (PikaColorScale *scale);
|
|
static void pika_color_scale_render_alpha (PikaColorScale *scale);
|
|
|
|
static void pika_color_scale_create_transform (PikaColorScale *scale);
|
|
static void pika_color_scale_destroy_transform (PikaColorScale *scale);
|
|
static void pika_color_scale_notify_config (PikaColorConfig *config,
|
|
const GParamSpec *pspec,
|
|
PikaColorScale *scale);
|
|
|
|
|
|
G_DEFINE_TYPE_WITH_PRIVATE (PikaColorScale, pika_color_scale, GTK_TYPE_SCALE)
|
|
|
|
#define parent_class pika_color_scale_parent_class
|
|
|
|
static const Babl *fish_rgb_to_lch = NULL;
|
|
static const Babl *fish_lch_to_rgb = NULL;
|
|
|
|
|
|
static void
|
|
pika_color_scale_class_init (PikaColorScaleClass *klass)
|
|
{
|
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
|
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
|
|
|
|
object_class->dispose = pika_color_scale_dispose;
|
|
object_class->finalize = pika_color_scale_finalize;
|
|
object_class->get_property = pika_color_scale_get_property;
|
|
object_class->set_property = pika_color_scale_set_property;
|
|
|
|
widget_class->size_allocate = pika_color_scale_size_allocate;
|
|
widget_class->draw = pika_color_scale_draw;
|
|
|
|
/**
|
|
* PikaColorScale: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",
|
|
PIKA_TYPE_COLOR_SELECTOR_CHANNEL,
|
|
PIKA_COLOR_SELECTOR_VALUE,
|
|
PIKA_PARAM_READWRITE |
|
|
G_PARAM_CONSTRUCT));
|
|
|
|
gtk_widget_class_set_css_name (widget_class, "PikaColorScale");
|
|
|
|
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
|
|
pika_color_scale_init (PikaColorScale *scale)
|
|
{
|
|
PikaColorScalePrivate *priv;
|
|
GtkRange *range = GTK_RANGE (scale);
|
|
GtkCssProvider *css;
|
|
|
|
scale->priv = pika_color_scale_get_instance_private (scale);
|
|
|
|
priv = scale->priv;
|
|
|
|
gtk_widget_set_can_focus (GTK_WIDGET (scale), TRUE);
|
|
|
|
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);
|
|
|
|
priv->channel = PIKA_COLOR_SELECTOR_VALUE;
|
|
priv->needs_render = TRUE;
|
|
|
|
gtk_orientable_set_orientation (GTK_ORIENTABLE (range),
|
|
GTK_ORIENTATION_HORIZONTAL);
|
|
|
|
pika_rgba_set (&priv->rgb, 0.0, 0.0, 0.0, 1.0);
|
|
pika_rgb_to_hsv (&priv->rgb, &priv->hsv);
|
|
|
|
pika_widget_track_monitor (GTK_WIDGET (scale),
|
|
G_CALLBACK (pika_color_scale_destroy_transform),
|
|
NULL, NULL);
|
|
|
|
css = gtk_css_provider_new ();
|
|
gtk_css_provider_load_from_data (css,
|
|
"PikaColorScale {"
|
|
" padding: 2px 12px 2px 12px;"
|
|
" min-width: 24px;"
|
|
" min-height: 24px;"
|
|
"}\n"
|
|
"PikaColorScale contents trough {"
|
|
" min-width: 20px;"
|
|
" min-height: 20px;"
|
|
"}\n"
|
|
"PikaColorScale contents trough slider {"
|
|
" min-width: 12px;"
|
|
" min-height: 12px;"
|
|
" margin: -6px -6px -6px -6px;"
|
|
"}",
|
|
-1, NULL);
|
|
gtk_style_context_add_provider (gtk_widget_get_style_context (GTK_WIDGET (scale)),
|
|
GTK_STYLE_PROVIDER (css),
|
|
GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
|
|
g_object_unref (css);
|
|
}
|
|
|
|
static void
|
|
pika_color_scale_dispose (GObject *object)
|
|
{
|
|
PikaColorScale *scale = PIKA_COLOR_SCALE (object);
|
|
|
|
pika_color_scale_set_color_config (scale, NULL);
|
|
|
|
G_OBJECT_CLASS (parent_class)->dispose (object);
|
|
}
|
|
|
|
static void
|
|
pika_color_scale_finalize (GObject *object)
|
|
{
|
|
PikaColorScalePrivate *priv = GET_PRIVATE (object);
|
|
|
|
g_clear_pointer (&priv->buf, g_free);
|
|
priv->width = 0;
|
|
priv->height = 0;
|
|
priv->rowstride = 0;
|
|
|
|
G_OBJECT_CLASS (parent_class)->finalize (object);
|
|
}
|
|
|
|
static void
|
|
pika_color_scale_get_property (GObject *object,
|
|
guint property_id,
|
|
GValue *value,
|
|
GParamSpec *pspec)
|
|
{
|
|
PikaColorScalePrivate *priv = GET_PRIVATE (object);
|
|
|
|
switch (property_id)
|
|
{
|
|
case PROP_CHANNEL:
|
|
g_value_set_enum (value, priv->channel);
|
|
break;
|
|
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
pika_color_scale_set_property (GObject *object,
|
|
guint property_id,
|
|
const GValue *value,
|
|
GParamSpec *pspec)
|
|
{
|
|
PikaColorScale *scale = PIKA_COLOR_SCALE (object);
|
|
|
|
switch (property_id)
|
|
{
|
|
case PROP_CHANNEL:
|
|
pika_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
|
|
pika_color_scale_size_allocate (GtkWidget *widget,
|
|
GtkAllocation *allocation)
|
|
{
|
|
PikaColorScalePrivate *priv = GET_PRIVATE (widget);
|
|
GtkRange *range = GTK_RANGE (widget);
|
|
GdkRectangle range_rect;
|
|
|
|
GTK_WIDGET_CLASS (parent_class)->size_allocate (widget, allocation);
|
|
|
|
gtk_range_get_range_rect (range, &range_rect);
|
|
|
|
if (range_rect.width != priv->width ||
|
|
range_rect.height != priv->height)
|
|
{
|
|
priv->width = range_rect.width;
|
|
priv->height = range_rect.height;
|
|
|
|
priv->rowstride = priv->width * 4;
|
|
|
|
g_free (priv->buf);
|
|
priv->buf = g_new (guchar, priv->rowstride * priv->height);
|
|
|
|
priv->needs_render = TRUE;
|
|
}
|
|
}
|
|
|
|
static gboolean
|
|
pika_color_scale_draw (GtkWidget *widget,
|
|
cairo_t *cr)
|
|
{
|
|
PikaColorScale *scale = PIKA_COLOR_SCALE (widget);
|
|
PikaColorScalePrivate *priv = GET_PRIVATE (widget);
|
|
GtkRange *range = GTK_RANGE (widget);
|
|
GtkStyleContext *context = gtk_widget_get_style_context (widget);
|
|
GdkRectangle range_rect;
|
|
GdkRectangle area = { 0, };
|
|
cairo_surface_t *buffer;
|
|
gint slider_start;
|
|
gint slider_end;
|
|
gint slider_mid;
|
|
gint slider_size;
|
|
|
|
if (! priv->buf)
|
|
return FALSE;
|
|
|
|
gtk_range_get_range_rect (range, &range_rect);
|
|
gtk_range_get_slider_range (range, &slider_start, &slider_end);
|
|
|
|
slider_mid = slider_start + (slider_end - slider_start) / 2;
|
|
slider_size = 6;
|
|
|
|
if (priv->needs_render)
|
|
{
|
|
pika_color_scale_render (scale);
|
|
|
|
priv->needs_render = FALSE;
|
|
}
|
|
|
|
if (! priv->transform)
|
|
pika_color_scale_create_transform (scale);
|
|
|
|
if (priv->transform)
|
|
{
|
|
const Babl *format = babl_format ("cairo-RGB24");
|
|
guchar *buf = g_new (guchar, priv->rowstride * priv->height);
|
|
guchar *src = priv->buf;
|
|
guchar *dest = buf;
|
|
guint i;
|
|
|
|
for (i = 0; i < priv->height; i++)
|
|
{
|
|
pika_color_transform_process_pixels (priv->transform,
|
|
format, src,
|
|
format, dest,
|
|
priv->width);
|
|
|
|
src += priv->rowstride;
|
|
dest += priv->rowstride;
|
|
}
|
|
|
|
buffer = cairo_image_surface_create_for_data (buf,
|
|
CAIRO_FORMAT_RGB24,
|
|
priv->width,
|
|
priv->height,
|
|
priv->rowstride);
|
|
cairo_surface_set_user_data (buffer, NULL,
|
|
buf, (cairo_destroy_func_t) g_free);
|
|
}
|
|
else
|
|
{
|
|
buffer = cairo_image_surface_create_for_data (priv->buf,
|
|
CAIRO_FORMAT_RGB24,
|
|
priv->width,
|
|
priv->height,
|
|
priv->rowstride);
|
|
}
|
|
|
|
switch (gtk_orientable_get_orientation (GTK_ORIENTABLE (range)))
|
|
{
|
|
case GTK_ORIENTATION_HORIZONTAL:
|
|
cairo_set_source_surface (cr, buffer,
|
|
range_rect.x, range_rect.y);
|
|
break;
|
|
|
|
case GTK_ORIENTATION_VERTICAL:
|
|
cairo_set_source_surface (cr, buffer,
|
|
range_rect.x, range_rect.y);
|
|
break;
|
|
}
|
|
|
|
cairo_surface_destroy (buffer);
|
|
|
|
if (! gtk_widget_is_sensitive (widget))
|
|
{
|
|
static cairo_pattern_t *pattern = NULL;
|
|
|
|
if (! pattern)
|
|
{
|
|
static const guchar stipple[] = { 0, 255, 0, 0,
|
|
255, 0, 0, 0 };
|
|
cairo_surface_t *surface;
|
|
gint stride;
|
|
|
|
stride = cairo_format_stride_for_width (CAIRO_FORMAT_A8, 2);
|
|
|
|
surface = cairo_image_surface_create_for_data ((guchar *) stipple,
|
|
CAIRO_FORMAT_A8,
|
|
2, 2, stride);
|
|
pattern = cairo_pattern_create_for_surface (surface);
|
|
cairo_surface_destroy (surface);
|
|
|
|
cairo_pattern_set_extend (pattern, CAIRO_EXTEND_REPEAT);
|
|
}
|
|
|
|
cairo_mask (cr, pattern);
|
|
}
|
|
else
|
|
{
|
|
cairo_paint (cr);
|
|
}
|
|
|
|
if (gtk_widget_has_focus (widget))
|
|
gtk_render_focus (context, cr,
|
|
0, 0,
|
|
gtk_widget_get_allocated_width (widget),
|
|
gtk_widget_get_allocated_height (widget));
|
|
|
|
switch (gtk_orientable_get_orientation (GTK_ORIENTABLE (range)))
|
|
{
|
|
case GTK_ORIENTATION_HORIZONTAL:
|
|
area.x = slider_mid - slider_size;
|
|
area.y = range_rect.y;
|
|
area.width = 2 * slider_size;
|
|
area.height = range_rect.height;
|
|
break;
|
|
|
|
case GTK_ORIENTATION_VERTICAL:
|
|
area.x = range_rect.x;
|
|
area.y = slider_mid - slider_size;
|
|
area.width = range_rect.width;
|
|
area.height = 2 * slider_size;
|
|
break;
|
|
}
|
|
|
|
if (gtk_widget_is_sensitive (widget))
|
|
cairo_set_source_rgb (cr, 0.0, 0.0, 0.0);
|
|
else
|
|
cairo_set_source_rgb (cr, 0.2, 0.2, 0.2);
|
|
|
|
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 + slider_size);
|
|
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 + slider_size,
|
|
area.y + area.height / 2 + 0.5);
|
|
break;
|
|
}
|
|
|
|
cairo_close_path (cr);
|
|
cairo_fill (cr);
|
|
|
|
if (gtk_widget_is_sensitive (widget))
|
|
cairo_set_source_rgb (cr, 1.0, 1.0, 1.0);
|
|
else
|
|
cairo_set_source_rgb (cr, 0.8, 0.8, 0.8);
|
|
|
|
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 - slider_size);
|
|
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 - slider_size,
|
|
area.y + area.height / 2 + 0.5);
|
|
break;
|
|
}
|
|
|
|
cairo_close_path (cr);
|
|
cairo_fill (cr);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/**
|
|
* pika_color_scale_new:
|
|
* @orientation: the scale's orientation (horizontal or vertical)
|
|
* @channel: the scale's color channel
|
|
*
|
|
* Creates a new #PikaColorScale widget.
|
|
*
|
|
* Returns: a new #PikaColorScale widget
|
|
**/
|
|
GtkWidget *
|
|
pika_color_scale_new (GtkOrientation orientation,
|
|
PikaColorSelectorChannel channel)
|
|
{
|
|
PikaColorScale *scale = g_object_new (PIKA_TYPE_COLOR_SCALE,
|
|
"orientation", orientation,
|
|
"channel", channel,
|
|
NULL);
|
|
|
|
gtk_range_set_flippable (GTK_RANGE (scale),
|
|
orientation == GTK_ORIENTATION_HORIZONTAL);
|
|
|
|
return GTK_WIDGET (scale);
|
|
}
|
|
|
|
/**
|
|
* pika_color_scale_set_channel:
|
|
* @scale: a #PikaColorScale widget
|
|
* @channel: the new color channel
|
|
*
|
|
* Changes the color channel displayed by the @scale.
|
|
**/
|
|
void
|
|
pika_color_scale_set_channel (PikaColorScale *scale,
|
|
PikaColorSelectorChannel channel)
|
|
{
|
|
PikaColorScalePrivate *priv;
|
|
|
|
g_return_if_fail (PIKA_IS_COLOR_SCALE (scale));
|
|
|
|
priv = GET_PRIVATE (scale);
|
|
|
|
if (channel != priv->channel)
|
|
{
|
|
priv->channel = channel;
|
|
|
|
priv->needs_render = TRUE;
|
|
gtk_widget_queue_draw (GTK_WIDGET (scale));
|
|
|
|
g_object_notify (G_OBJECT (scale), "channel");
|
|
}
|
|
}
|
|
|
|
/**
|
|
* pika_color_scale_set_color:
|
|
* @scale: a #PikaColorScale widget
|
|
* @rgb: the new color as #PikaRGB
|
|
* @hsv: the new color as #PikaHSV
|
|
*
|
|
* Changes the color value of the @scale.
|
|
**/
|
|
void
|
|
pika_color_scale_set_color (PikaColorScale *scale,
|
|
const PikaRGB *rgb,
|
|
const PikaHSV *hsv)
|
|
{
|
|
PikaColorScalePrivate *priv;
|
|
|
|
g_return_if_fail (PIKA_IS_COLOR_SCALE (scale));
|
|
g_return_if_fail (rgb != NULL);
|
|
g_return_if_fail (hsv != NULL);
|
|
|
|
priv = GET_PRIVATE (scale);
|
|
|
|
priv->rgb = *rgb;
|
|
priv->hsv = *hsv;
|
|
|
|
priv->needs_render = TRUE;
|
|
gtk_widget_queue_draw (GTK_WIDGET (scale));
|
|
}
|
|
|
|
/**
|
|
* pika_color_scale_set_color_config:
|
|
* @scale: a #PikaColorScale widget.
|
|
* @config: a #PikaColorConfig object.
|
|
*
|
|
* Sets the color management configuration to use with this color scale.
|
|
*
|
|
* Since: 2.10
|
|
*/
|
|
void
|
|
pika_color_scale_set_color_config (PikaColorScale *scale,
|
|
PikaColorConfig *config)
|
|
{
|
|
PikaColorScalePrivate *priv;
|
|
|
|
g_return_if_fail (PIKA_IS_COLOR_SCALE (scale));
|
|
g_return_if_fail (config == NULL || PIKA_IS_COLOR_CONFIG (config));
|
|
|
|
priv = GET_PRIVATE (scale);
|
|
|
|
if (config != priv->config)
|
|
{
|
|
if (priv->config)
|
|
{
|
|
g_signal_handlers_disconnect_by_func (priv->config,
|
|
pika_color_scale_notify_config,
|
|
scale);
|
|
|
|
pika_color_scale_destroy_transform (scale);
|
|
}
|
|
|
|
g_set_object (&priv->config, config);
|
|
|
|
if (priv->config)
|
|
{
|
|
g_signal_connect (priv->config, "notify",
|
|
G_CALLBACK (pika_color_scale_notify_config),
|
|
scale);
|
|
|
|
pika_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
|
|
pika_color_scale_render (PikaColorScale *scale)
|
|
{
|
|
PikaColorScalePrivate *priv = GET_PRIVATE (scale);
|
|
GtkRange *range = GTK_RANGE (scale);
|
|
PikaRGB rgb;
|
|
PikaHSV hsv;
|
|
PikaLCH 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 = priv->buf) == NULL)
|
|
return;
|
|
|
|
if (priv->channel == PIKA_COLOR_SELECTOR_ALPHA)
|
|
{
|
|
pika_color_scale_render_alpha (scale);
|
|
return;
|
|
}
|
|
|
|
rgb = priv->rgb;
|
|
hsv = priv->hsv;
|
|
babl_process (fish_rgb_to_lch, &rgb, &lch, 1);
|
|
|
|
switch (priv->channel)
|
|
{
|
|
case PIKA_COLOR_SELECTOR_HUE: channel_value = &hsv.h; break;
|
|
case PIKA_COLOR_SELECTOR_SATURATION: channel_value = &hsv.s; break;
|
|
case PIKA_COLOR_SELECTOR_VALUE: channel_value = &hsv.v; break;
|
|
|
|
case PIKA_COLOR_SELECTOR_RED: channel_value = &rgb.r; break;
|
|
case PIKA_COLOR_SELECTOR_GREEN: channel_value = &rgb.g; break;
|
|
case PIKA_COLOR_SELECTOR_BLUE: channel_value = &rgb.b; break;
|
|
case PIKA_COLOR_SELECTOR_ALPHA: channel_value = &rgb.a; break;
|
|
|
|
case PIKA_COLOR_SELECTOR_LCH_LIGHTNESS: channel_value = &lch.l; break;
|
|
case PIKA_COLOR_SELECTOR_LCH_CHROMA: channel_value = &lch.c; break;
|
|
case PIKA_COLOR_SELECTOR_LCH_HUE: channel_value = &lch.h; break;
|
|
}
|
|
|
|
switch (priv->channel)
|
|
{
|
|
case PIKA_COLOR_SELECTOR_HUE:
|
|
case PIKA_COLOR_SELECTOR_SATURATION:
|
|
case PIKA_COLOR_SELECTOR_VALUE:
|
|
from_hsv = TRUE;
|
|
break;
|
|
|
|
case PIKA_COLOR_SELECTOR_LCH_LIGHTNESS:
|
|
multiplier = 100;
|
|
from_lch = TRUE;
|
|
break;
|
|
case PIKA_COLOR_SELECTOR_LCH_CHROMA:
|
|
multiplier = 200;
|
|
from_lch = TRUE;
|
|
break;
|
|
case PIKA_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 < priv->width; x++, d += 4)
|
|
{
|
|
gdouble value = (gdouble) x * multiplier / (gdouble) (priv->width - 1);
|
|
guchar r, g, b;
|
|
|
|
if (invert)
|
|
value = multiplier - value;
|
|
|
|
*channel_value = value;
|
|
|
|
if (from_hsv)
|
|
pika_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
|
|
{
|
|
pika_rgb_get_uchar (&rgb, &r, &g, &b);
|
|
}
|
|
|
|
PIKA_CAIRO_RGB24_SET_PIXEL (d, r, g, b);
|
|
}
|
|
|
|
d = buf + priv->rowstride;
|
|
for (y = 1; y < priv->height; y++)
|
|
{
|
|
memcpy (d, buf, priv->rowstride);
|
|
d += priv->rowstride;
|
|
}
|
|
break;
|
|
|
|
case GTK_ORIENTATION_VERTICAL:
|
|
for (y = 0; y < priv->height; y++)
|
|
{
|
|
gdouble value = (gdouble) y * multiplier / (gdouble) (priv->height - 1);
|
|
guchar r, g, b;
|
|
|
|
if (invert)
|
|
value = multiplier - value;
|
|
|
|
*channel_value = value;
|
|
|
|
if (from_hsv)
|
|
pika_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
|
|
{
|
|
pika_rgb_get_uchar (&rgb, &r, &g, &b);
|
|
}
|
|
|
|
for (x = 0, d = buf; x < priv->width; x++, d += 4)
|
|
{
|
|
PIKA_CAIRO_RGB24_SET_PIXEL (d, r, g, b);
|
|
}
|
|
|
|
buf += priv->rowstride;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
pika_color_scale_render_alpha (PikaColorScale *scale)
|
|
{
|
|
PikaColorScalePrivate *priv = GET_PRIVATE (scale);
|
|
GtkRange *range = GTK_RANGE (scale);
|
|
PikaRGB rgb;
|
|
gboolean invert;
|
|
gdouble a;
|
|
guint x, y;
|
|
guchar *buf;
|
|
guchar *d, *l;
|
|
|
|
invert = should_invert (range);
|
|
|
|
buf = priv->buf;
|
|
rgb = priv->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 = (priv->height > PIKA_CHECK_SIZE_SM ?
|
|
buf + PIKA_CHECK_SIZE_SM * priv->rowstride : light);
|
|
|
|
for (x = 0, d = light, l = dark; x < priv->width; x++)
|
|
{
|
|
if ((x % PIKA_CHECK_SIZE_SM) == 0)
|
|
{
|
|
guchar *t;
|
|
|
|
t = d;
|
|
d = l;
|
|
l = t;
|
|
}
|
|
|
|
a = (gdouble) x / (gdouble) (priv->width - 1);
|
|
|
|
if (invert)
|
|
a = 1.0 - a;
|
|
|
|
PIKA_CAIRO_RGB24_SET_PIXEL (l,
|
|
(PIKA_CHECK_LIGHT +
|
|
(rgb.r - PIKA_CHECK_LIGHT) * a) * 255.999,
|
|
(PIKA_CHECK_LIGHT +
|
|
(rgb.g - PIKA_CHECK_LIGHT) * a) * 255.999,
|
|
(PIKA_CHECK_LIGHT +
|
|
(rgb.b - PIKA_CHECK_LIGHT) * a) * 255.999);
|
|
l += 4;
|
|
|
|
PIKA_CAIRO_RGB24_SET_PIXEL (d,
|
|
(PIKA_CHECK_DARK +
|
|
(rgb.r - PIKA_CHECK_DARK) * a) * 255.999,
|
|
(PIKA_CHECK_DARK +
|
|
(rgb.g - PIKA_CHECK_DARK) * a) * 255.999,
|
|
(PIKA_CHECK_DARK +
|
|
(rgb.b - PIKA_CHECK_DARK) * a) * 255.999);
|
|
d += 4;
|
|
}
|
|
|
|
for (y = 0, d = buf; y < priv->height; y++, d += priv->rowstride)
|
|
{
|
|
if (y == 0 || y == PIKA_CHECK_SIZE_SM)
|
|
continue;
|
|
|
|
if ((y / PIKA_CHECK_SIZE_SM) & 1)
|
|
memcpy (d, dark, priv->rowstride);
|
|
else
|
|
memcpy (d, light, priv->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 < priv->height; y++, d += priv->rowstride)
|
|
{
|
|
a = (gdouble) y / (gdouble) (priv->height - 1);
|
|
|
|
if (invert)
|
|
a = 1.0 - a;
|
|
|
|
PIKA_CAIRO_RGB24_SET_PIXEL (light,
|
|
(PIKA_CHECK_LIGHT +
|
|
(rgb.r - PIKA_CHECK_LIGHT) * a) * 255.999,
|
|
(PIKA_CHECK_LIGHT +
|
|
(rgb.g - PIKA_CHECK_LIGHT) * a) * 255.999,
|
|
(PIKA_CHECK_LIGHT +
|
|
(rgb.b - PIKA_CHECK_LIGHT) * a) * 255.999);
|
|
|
|
PIKA_CAIRO_RGB24_SET_PIXEL (dark,
|
|
(PIKA_CHECK_DARK +
|
|
(rgb.r - PIKA_CHECK_DARK) * a) * 255.999,
|
|
(PIKA_CHECK_DARK +
|
|
(rgb.g - PIKA_CHECK_DARK) * a) * 255.999,
|
|
(PIKA_CHECK_DARK +
|
|
(rgb.b - PIKA_CHECK_DARK) * a) * 255.999);
|
|
|
|
for (x = 0, l = d; x < priv->width; x++, l += 4)
|
|
{
|
|
if (((x / PIKA_CHECK_SIZE_SM) ^ (y / PIKA_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;
|
|
}
|
|
}
|
|
|
|
static void
|
|
pika_color_scale_create_transform (PikaColorScale *scale)
|
|
{
|
|
PikaColorScalePrivate *priv = GET_PRIVATE (scale);
|
|
|
|
if (priv->config)
|
|
{
|
|
static PikaColorProfile *profile = NULL;
|
|
|
|
const Babl *format = babl_format ("cairo-RGB24");
|
|
|
|
if (G_UNLIKELY (! profile))
|
|
profile = pika_color_profile_new_rgb_srgb ();
|
|
|
|
priv->transform = pika_widget_get_color_transform (GTK_WIDGET (scale),
|
|
priv->config,
|
|
profile,
|
|
format,
|
|
format,
|
|
NULL,
|
|
PIKA_COLOR_RENDERING_INTENT_RELATIVE_COLORIMETRIC,
|
|
FALSE);
|
|
}
|
|
}
|
|
|
|
static void
|
|
pika_color_scale_destroy_transform (PikaColorScale *scale)
|
|
{
|
|
PikaColorScalePrivate *priv = GET_PRIVATE (scale);
|
|
|
|
if (priv->transform)
|
|
{
|
|
g_object_unref (priv->transform);
|
|
priv->transform = NULL;
|
|
}
|
|
|
|
gtk_widget_queue_draw (GTK_WIDGET (scale));
|
|
}
|
|
|
|
static void
|
|
pika_color_scale_notify_config (PikaColorConfig *config,
|
|
const GParamSpec *pspec,
|
|
PikaColorScale *scale)
|
|
{
|
|
PikaColorScalePrivate *priv = GET_PRIVATE (scale);
|
|
PikaRGB color;
|
|
|
|
pika_color_scale_destroy_transform (scale);
|
|
|
|
pika_color_config_get_out_of_gamut_color (config, &color);
|
|
pika_rgb_get_uchar (&color,
|
|
priv->oog_color,
|
|
priv->oog_color + 1,
|
|
priv->oog_color + 2);
|
|
priv->needs_render = TRUE;
|
|
}
|