/* LIBGIMP - The GIMP Library * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball * * gimpcolorscale.c * Copyright (C) 2002-2010 Sven Neumann * Michael Natterer * * 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 * . */ #include "config.h" #include #include #include #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; }