/* PIKA - Photo and Image Kooker Application * a rebranding of The GNU Image Manipulation Program (created with heckimp) * A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio * * Original copyright, applying to most contents (license remains unchanged): * Copyright (C) 1995 Spencer Kimball and Peter Mattis * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "config.h" #include #include #include #include "libpikabase/pikabase.h" #include "libpikawidgets/pikawidgets.h" #include "libpikamath/pikamath.h" #include "libpikacolor/pikacolor.h" #include "libpikaconfig/pikaconfig.h" #include "widgets-types.h" #include "pikacolorbar.h" enum { PROP_0, PROP_ORIENTATION, PROP_COLOR, PROP_CHANNEL }; /* local function prototypes */ static void pika_color_bar_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec); static void pika_color_bar_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec); static gboolean pika_color_bar_draw (GtkWidget *widget, cairo_t *cr); G_DEFINE_TYPE (PikaColorBar, pika_color_bar, GTK_TYPE_EVENT_BOX) #define parent_class pika_color_bar_parent_class static void pika_color_bar_class_init (PikaColorBarClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); PikaRGB white = { 1.0, 1.0, 1.0, 1.0 }; object_class->set_property = pika_color_bar_set_property; object_class->get_property = pika_color_bar_get_property; widget_class->draw = pika_color_bar_draw; g_object_class_install_property (object_class, PROP_ORIENTATION, g_param_spec_enum ("orientation", NULL, NULL, GTK_TYPE_ORIENTATION, GTK_ORIENTATION_HORIZONTAL, PIKA_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); g_object_class_install_property (object_class, PROP_COLOR, pika_param_spec_rgb ("color", NULL, NULL, FALSE, &white, PIKA_PARAM_WRITABLE | G_PARAM_CONSTRUCT)); g_object_class_install_property (object_class, PROP_CHANNEL, g_param_spec_enum ("histogram-channel", NULL, NULL, PIKA_TYPE_HISTOGRAM_CHANNEL, PIKA_HISTOGRAM_VALUE, PIKA_PARAM_WRITABLE)); } static void pika_color_bar_init (PikaColorBar *bar) { gtk_event_box_set_visible_window (GTK_EVENT_BOX (bar), FALSE); bar->orientation = GTK_ORIENTATION_HORIZONTAL; } static void pika_color_bar_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { PikaColorBar *bar = PIKA_COLOR_BAR (object); switch (property_id) { case PROP_ORIENTATION: bar->orientation = g_value_get_enum (value); break; case PROP_COLOR: pika_color_bar_set_color (bar, g_value_get_boxed (value)); break; case PROP_CHANNEL: pika_color_bar_set_channel (bar, g_value_get_enum (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void pika_color_bar_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { PikaColorBar *bar = PIKA_COLOR_BAR (object); switch (property_id) { case PROP_ORIENTATION: g_value_set_enum (value, bar->orientation); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static gboolean pika_color_bar_draw (GtkWidget *widget, cairo_t *cr) { PikaColorBar *bar = PIKA_COLOR_BAR (widget); GtkAllocation allocation; cairo_surface_t *surface; cairo_pattern_t *pattern; guchar *src; guchar *dest; gint x, y; gint width, height; gint i; gtk_widget_get_allocation (widget, &allocation); x = y = gtk_container_get_border_width (GTK_CONTAINER (bar)); width = allocation.width - 2 * x; height = allocation.height - 2 * y; if (width < 1 || height < 1) return TRUE; cairo_translate (cr, x, y); cairo_rectangle (cr, 0, 0, width, height); cairo_clip (cr); surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24, 256, 1); for (i = 0, src = bar->buf, dest = cairo_image_surface_get_data (surface); i < 256; i++, src += 3, dest += 4) { PIKA_CAIRO_RGB24_SET_PIXEL(dest, src[0], src[1], src[2]); } cairo_surface_mark_dirty (surface); pattern = cairo_pattern_create_for_surface (surface); cairo_pattern_set_extend (pattern, CAIRO_EXTEND_REFLECT); cairo_surface_destroy (surface); if (bar->orientation == GTK_ORIENTATION_HORIZONTAL) { cairo_scale (cr, (gdouble) width / 256.0, 1.0); } else { cairo_translate (cr, 0, height); cairo_scale (cr, 1.0, (gdouble) height / 256.0); cairo_rotate (cr, - G_PI / 2); } cairo_set_source (cr, pattern); cairo_pattern_destroy (pattern); cairo_paint (cr); return TRUE; } /* public functions */ /** * pika_color_bar_new: * @orientation: whether the bar should be oriented horizontally or * vertically * * Creates a new #PikaColorBar widget. * * Returns: The new #PikaColorBar widget. **/ GtkWidget * pika_color_bar_new (GtkOrientation orientation) { return g_object_new (PIKA_TYPE_COLOR_BAR, "orientation", orientation, NULL); } /** * pika_color_bar_set_color: * @bar: a #PikaColorBar widget * @color: a #PikaRGB color * * Makes the @bar display a gradient from black (on the left or the * bottom), to the given @color (on the right or at the top). **/ void pika_color_bar_set_color (PikaColorBar *bar, const PikaRGB *color) { guchar *buf; gint i; g_return_if_fail (PIKA_IS_COLOR_BAR (bar)); g_return_if_fail (color != NULL); for (i = 0, buf = bar->buf; i < 256; i++, buf += 3) { buf[0] = ROUND (color->r * (gdouble) i); buf[1] = ROUND (color->g * (gdouble) i); buf[2] = ROUND (color->b * (gdouble) i); } gtk_widget_queue_draw (GTK_WIDGET (bar)); } /** * pika_color_bar_set_channel: * @bar: a #PikaColorBar widget * @channel: a #PikaHistogramChannel * * Convenience function that calls pika_color_bar_set_color() with the * color that matches the @channel. **/ void pika_color_bar_set_channel (PikaColorBar *bar, PikaHistogramChannel channel) { PikaRGB color = { 1.0, 1.0, 1.0, 1.0 }; g_return_if_fail (PIKA_IS_COLOR_BAR (bar)); switch (channel) { case PIKA_HISTOGRAM_VALUE: case PIKA_HISTOGRAM_LUMINANCE: case PIKA_HISTOGRAM_ALPHA: case PIKA_HISTOGRAM_RGB: pika_rgb_set (&color, 1.0, 1.0, 1.0); break; case PIKA_HISTOGRAM_RED: pika_rgb_set (&color, 1.0, 0.0, 0.0); break; case PIKA_HISTOGRAM_GREEN: pika_rgb_set (&color, 0.0, 1.0, 0.0); break; case PIKA_HISTOGRAM_BLUE: pika_rgb_set (&color, 0.0, 0.0, 1.0); break; } pika_color_bar_set_color (bar, &color); } /** * pika_color_bar_set_buffers: * @bar: a #PikaColorBar widget * @red: an array of 256 values * @green: an array of 256 values * @blue: an array of 256 values * * This function gives full control over the colors displayed by the * @bar widget. The 3 arrays can for example be taken from a #Levels * or a #Curves struct. **/ void pika_color_bar_set_buffers (PikaColorBar *bar, const guchar *red, const guchar *green, const guchar *blue) { guchar *buf; gint i; g_return_if_fail (PIKA_IS_COLOR_BAR (bar)); g_return_if_fail (red != NULL); g_return_if_fail (green != NULL); g_return_if_fail (blue != NULL); for (i = 0, buf = bar->buf; i < 256; i++, buf += 3) { buf[0] = red[i]; buf[1] = green[i]; buf[2] = blue[i]; } gtk_widget_queue_draw (GTK_WIDGET (bar)); }