1
1
mirror of https://gitlab.gnome.org/GNOME/gimp.git synced 2025-10-06 01:12:40 +02:00
Files
gimp/app/tools/gimplevelstool.c
Alx Sa 3879520cc2 tools, operation: Add support for loading PS Levels presets
Photoshop stores their Levels filter presets in an
.alv file. This patch adds support for loading these
and converting them to GIMP's Level format.

The .alv file is binary, and consists of a 2 byte version
followed by 29 groups of 2 byte values for input,
output, and gamma. We only read the first four for
now, since GIMP only supports composite and then
R, G, and B individual channels.
2025-05-22 18:52:30 +00:00

1064 lines
38 KiB
C

/* GIMP - The GNU Image Manipulation Program
* 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 <https://www.gnu.org/licenses/>.
*/
#include "config.h"
#include <gegl.h>
#include <gtk/gtk.h>
#include "libgimpbase/gimpbase.h"
#include "libgimpmath/gimpmath.h"
#include "libgimpconfig/gimpconfig.h"
#include "libgimpwidgets/gimpwidgets.h"
#include "tools-types.h"
#include "gegl/gimp-babl.h"
#include "operations/gimplevelsconfig.h"
#include "operations/gimpoperationlevels.h"
#include "core/gimp-gui.h"
#include "core/gimpasync.h"
#include "core/gimpcancelable.h"
#include "core/gimpdata.h"
#include "core/gimpdrawable.h"
#include "core/gimpdrawable-histogram.h"
#include "core/gimperror.h"
#include "core/gimphistogram.h"
#include "core/gimpimage.h"
#include "core/gimptoolinfo.h"
#include "core/gimptriviallycancelablewaitable.h"
#include "core/gimpwaitable.h"
#include "widgets/gimpcolorbar.h"
#include "widgets/gimphandlebar.h"
#include "widgets/gimphelp-ids.h"
#include "widgets/gimphistogramview.h"
#include "widgets/gimppropwidgets.h"
#include "widgets/gimpwidgets-constructors.h"
#include "display/gimpdisplay.h"
#include "gimphistogramoptions.h"
#include "gimplevelstool.h"
#include "gimp-intl.h"
#define PICK_LOW_INPUT (1 << 0)
#define PICK_GAMMA (1 << 1)
#define PICK_HIGH_INPUT (1 << 2)
#define PICK_ALL_CHANNELS (1 << 8)
#define HISTOGRAM_WIDTH 256
#define GRADIENT_HEIGHT 12
#define CONTROL_HEIGHT 10
/* local function prototypes */
static void gimp_levels_tool_finalize (GObject *object);
static gboolean gimp_levels_tool_initialize (GimpTool *tool,
GimpDisplay *display,
GError **error);
static gchar * gimp_levels_tool_get_operation (GimpFilterTool *filter_tool,
gchar **description);
static void gimp_levels_tool_dialog (GimpFilterTool *filter_tool);
static void gimp_levels_tool_reset (GimpFilterTool *filter_tool);
static void gimp_levels_tool_config_notify (GimpFilterTool *filter_tool,
GimpConfig *config,
const GParamSpec *pspec);
static gboolean gimp_levels_tool_settings_import(GimpFilterTool *filter_tool,
GInputStream *input,
GError **error);
static gboolean gimp_levels_tool_settings_export(GimpFilterTool *filter_tool,
GOutputStream *output,
GError **error);
static void gimp_levels_tool_color_picked (GimpFilterTool *filter_tool,
gpointer identifier,
gdouble x,
gdouble y,
const Babl *sample_format,
GeglColor *color);
static void gimp_levels_tool_export_setup (GimpSettingsBox *settings_box,
GtkFileChooserDialog *dialog,
gboolean export,
GimpLevelsTool *tool);
static void levels_update_input_bar (GimpLevelsTool *tool);
static void levels_channel_callback (GtkWidget *widget,
GimpFilterTool *filter_tool);
static void levels_channel_reset_callback (GtkWidget *widget,
GimpFilterTool *filter_tool);
static gboolean levels_menu_sensitivity (gint value,
gpointer data);
static void levels_stretch_callback (GtkWidget *widget,
GimpLevelsTool *tool);
static void levels_linear_gamma_changed (GtkAdjustment *adjustment,
GimpLevelsTool *tool);
static void levels_to_curves_callback (GtkWidget *widget,
GimpFilterTool *filter_tool);
G_DEFINE_TYPE (GimpLevelsTool, gimp_levels_tool, GIMP_TYPE_FILTER_TOOL)
#define parent_class gimp_levels_tool_parent_class
void
gimp_levels_tool_register (GimpToolRegisterCallback callback,
gpointer data)
{
(* callback) (GIMP_TYPE_LEVELS_TOOL,
GIMP_TYPE_HISTOGRAM_OPTIONS,
gimp_color_options_gui,
0,
"gimp-levels-tool",
_("Levels"),
_("Adjust color levels"),
N_("_Levels..."), NULL,
NULL, GIMP_HELP_TOOL_LEVELS,
GIMP_ICON_TOOL_LEVELS,
data);
}
static void
gimp_levels_tool_class_init (GimpLevelsToolClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
GimpToolClass *tool_class = GIMP_TOOL_CLASS (klass);
GimpFilterToolClass *filter_tool_class = GIMP_FILTER_TOOL_CLASS (klass);
object_class->finalize = gimp_levels_tool_finalize;
tool_class->initialize = gimp_levels_tool_initialize;
filter_tool_class->get_operation = gimp_levels_tool_get_operation;
filter_tool_class->dialog = gimp_levels_tool_dialog;
filter_tool_class->reset = gimp_levels_tool_reset;
filter_tool_class->config_notify = gimp_levels_tool_config_notify;
filter_tool_class->settings_import = gimp_levels_tool_settings_import;
filter_tool_class->settings_export = gimp_levels_tool_settings_export;
filter_tool_class->color_picked = gimp_levels_tool_color_picked;
}
static void
gimp_levels_tool_init (GimpLevelsTool *tool)
{
}
static void
gimp_levels_tool_finalize (GObject *object)
{
GimpLevelsTool *tool = GIMP_LEVELS_TOOL (object);
g_clear_object (&tool->histogram);
g_clear_object (&tool->histogram_async);
G_OBJECT_CLASS (parent_class)->finalize (object);
}
static gboolean
gimp_levels_tool_initialize (GimpTool *tool,
GimpDisplay *display,
GError **error)
{
GimpFilterTool *filter_tool = GIMP_FILTER_TOOL (tool);
GimpLevelsTool *l_tool = GIMP_LEVELS_TOOL (tool);
GimpImage *image = gimp_display_get_image (display);
GList *drawables;
GimpDrawable *drawable;
GimpLevelsConfig *config;
gdouble scale_factor;
gdouble step_increment;
gdouble page_increment;
gint digits;
if (! GIMP_TOOL_CLASS (parent_class)->initialize (tool, display, error))
{
return FALSE;
}
drawables = gimp_image_get_selected_drawables (image);
if (g_list_length (drawables) != 1)
{
if (g_list_length (drawables) > 1)
gimp_tool_message_literal (tool, display,
_("Cannot modify multiple drawables. Select only one."));
else
gimp_tool_message_literal (tool, display, _("No selected drawables."));
g_list_free (drawables);
return FALSE;
}
drawable = drawables->data;
g_list_free (drawables);
config = GIMP_LEVELS_CONFIG (filter_tool->config);
g_clear_object (&l_tool->histogram);
g_clear_object (&l_tool->histogram_async);
l_tool->histogram = gimp_histogram_new (config->trc);
l_tool->histogram_async = gimp_drawable_calculate_histogram_async
(drawable, l_tool->histogram, FALSE);
gimp_histogram_view_set_histogram (GIMP_HISTOGRAM_VIEW (l_tool->histogram_view),
l_tool->histogram);
if (gimp_drawable_get_component_type (drawable) == GIMP_COMPONENT_TYPE_U8)
{
scale_factor = 255.0;
step_increment = 1.0;
page_increment = 8.0;
digits = 0;
}
else
{
scale_factor = 100;
step_increment = 0.01;
page_increment = 1.0;
digits = 2;
}
gimp_prop_widget_set_factor (l_tool->low_input_spinbutton,
scale_factor, step_increment, page_increment,
digits);
gimp_prop_widget_set_factor (l_tool->high_input_spinbutton,
scale_factor, step_increment, page_increment,
digits);
gimp_prop_widget_set_factor (l_tool->low_output_spinbutton,
scale_factor, step_increment, page_increment,
digits);
gimp_prop_widget_set_factor (l_tool->high_output_spinbutton,
scale_factor, step_increment, page_increment,
digits);
gtk_adjustment_configure (l_tool->gamma_linear,
scale_factor / 2.0,
0, scale_factor, 0.1, 1.0, 0);
return TRUE;
}
static gchar *
gimp_levels_tool_get_operation (GimpFilterTool *filter_tool,
gchar **description)
{
*description = g_strdup (_("Adjust Color Levels"));
return g_strdup ("gimp:levels");
}
/*******************/
/* Levels dialog */
/*******************/
static GtkWidget *
gimp_levels_tool_color_picker_new (GimpLevelsTool *tool,
guint value)
{
const gchar *icon_name;
const gchar *help;
gboolean all_channels = (value & PICK_ALL_CHANNELS) != 0;
switch (value & 0xF)
{
case PICK_LOW_INPUT:
icon_name = GIMP_ICON_COLOR_PICKER_BLACK;
if (all_channels)
help = _("Pick black point for all channels");
else
help = _("Pick black point for the selected channel");
break;
case PICK_GAMMA:
icon_name = GIMP_ICON_COLOR_PICKER_GRAY;
if (all_channels)
help = _("Pick gray point for all channels");
else
help = _("Pick gray point for the selected channel");
break;
case PICK_HIGH_INPUT:
icon_name = GIMP_ICON_COLOR_PICKER_WHITE;
if (all_channels)
help = _("Pick white point for all channels");
else
help = _("Pick white point for the selected channel");
break;
default:
return NULL;
}
return gimp_filter_tool_add_color_picker (GIMP_FILTER_TOOL (tool),
GUINT_TO_POINTER (value),
icon_name,
help,
/* pick_abyss = */ FALSE,
NULL, NULL);
}
static void
gimp_levels_tool_dialog (GimpFilterTool *filter_tool)
{
GimpLevelsTool *tool = GIMP_LEVELS_TOOL (filter_tool);
GimpToolOptions *tool_options = GIMP_TOOL_GET_OPTIONS (filter_tool);
GimpLevelsConfig *config = GIMP_LEVELS_CONFIG (filter_tool->config);
GtkListStore *store;
GtkWidget *main_vbox;
GtkWidget *frame_vbox;
GtkWidget *vbox;
GtkWidget *vbox2;
GtkWidget *vbox3;
GtkWidget *hbox;
GtkWidget *hbox2;
GtkWidget *label;
GtkWidget *main_frame;
GtkWidget *frame;
GtkWidget *button;
GtkWidget *spinbutton;
GtkAdjustment *adjustment;
GtkWidget *bar;
GtkWidget *handle_bar;
gint border;
g_signal_connect (filter_tool->settings_box, "file-dialog-setup",
G_CALLBACK (gimp_levels_tool_export_setup),
filter_tool);
main_vbox = gimp_filter_tool_dialog_get_vbox (filter_tool);
/* The combo box for selecting channels */
main_frame = gimp_frame_new (NULL);
gtk_box_pack_start (GTK_BOX (main_vbox), main_frame, TRUE, TRUE, 0);
gtk_widget_show (main_frame);
hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
gtk_frame_set_label_widget (GTK_FRAME (main_frame), hbox);
gtk_widget_show (hbox);
label = gtk_label_new_with_mnemonic (_("Cha_nnel:"));
gimp_label_set_attributes (GTK_LABEL (label),
PANGO_ATTR_WEIGHT, PANGO_WEIGHT_BOLD,
-1);
gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
gtk_widget_show (label);
store = gimp_enum_store_new_with_range (GIMP_TYPE_HISTOGRAM_CHANNEL,
GIMP_HISTOGRAM_VALUE,
GIMP_HISTOGRAM_ALPHA);
g_set_weak_pointer (&tool->channel_menu,
gimp_enum_combo_box_new_with_model (GIMP_ENUM_STORE (store)));
g_object_unref (store);
gimp_enum_combo_box_set_icon_prefix (GIMP_ENUM_COMBO_BOX (tool->channel_menu),
"gimp-channel");
gimp_int_combo_box_set_sensitivity (GIMP_INT_COMBO_BOX (tool->channel_menu),
levels_menu_sensitivity, filter_tool, NULL);
gtk_box_pack_start (GTK_BOX (hbox), tool->channel_menu, FALSE, FALSE, 0);
gtk_widget_show (tool->channel_menu);
g_signal_connect (tool->channel_menu, "changed",
G_CALLBACK (levels_channel_callback),
tool);
gtk_label_set_mnemonic_widget (GTK_LABEL (label), tool->channel_menu);
button = gtk_button_new_with_mnemonic (_("R_eset Channel"));
gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
gtk_widget_show (button);
g_signal_connect (button, "clicked",
G_CALLBACK (levels_channel_reset_callback),
tool);
/* The histogram scale radio buttons */
hbox2 = gimp_prop_enum_icon_box_new (G_OBJECT (tool_options),
"histogram-scale", "gimp-histogram",
0, 0);
gtk_box_pack_end (GTK_BOX (hbox), hbox2, FALSE, FALSE, 0);
/* The linear/perceptual radio buttons */
hbox2 = gimp_prop_enum_icon_box_new (G_OBJECT (config), "trc",
"gimp-color-space",
-1, -1);
gtk_box_pack_end (GTK_BOX (hbox), hbox2, FALSE, FALSE, 0);
frame_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 4);
gtk_container_add (GTK_CONTAINER (main_frame), frame_vbox);
gtk_widget_show (frame_vbox);
/* Input levels frame */
frame = gimp_frame_new (_("Input Levels"));
gtk_box_pack_start (GTK_BOX (frame_vbox), frame, TRUE, TRUE, 0);
gtk_widget_show (frame);
vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 2);
gtk_container_add (GTK_CONTAINER (frame), vbox);
gtk_widget_show (vbox);
frame = gtk_frame_new (NULL);
gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
gtk_box_pack_start (GTK_BOX (vbox), frame, TRUE, TRUE, 0);
gtk_widget_show (frame);
vbox2 = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
gtk_container_add (GTK_CONTAINER (frame), vbox2);
gtk_widget_show (vbox2);
g_set_weak_pointer (&tool->histogram_view,
gimp_histogram_view_new (FALSE));
gtk_box_pack_start (GTK_BOX (vbox2), tool->histogram_view, TRUE, TRUE, 0);
gtk_widget_show (GTK_WIDGET (tool->histogram_view));
g_object_bind_property (G_OBJECT (tool_options), "histogram-scale",
G_OBJECT (tool->histogram_view), "histogram-scale",
G_BINDING_SYNC_CREATE |
G_BINDING_BIDIRECTIONAL);
g_object_get (tool->histogram_view, "border-width", &border, NULL);
vbox3 = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
gtk_container_set_border_width (GTK_CONTAINER (vbox3), border);
gtk_box_pack_start (GTK_BOX (vbox2), vbox3, FALSE, FALSE, 0);
gtk_widget_show (vbox3);
tool->input_bar = g_object_new (GIMP_TYPE_COLOR_BAR, NULL);
gtk_widget_set_size_request (tool->input_bar, -1, GRADIENT_HEIGHT / 2);
gtk_box_pack_start (GTK_BOX (vbox3), tool->input_bar, FALSE, FALSE, 0);
gtk_widget_show (tool->input_bar);
bar = g_object_new (GIMP_TYPE_COLOR_BAR, NULL);
gtk_widget_set_size_request (bar, -1, GRADIENT_HEIGHT / 2);
gtk_box_pack_start (GTK_BOX (vbox3), bar, FALSE, FALSE, 0);
gtk_widget_show (bar);
handle_bar = g_object_new (GIMP_TYPE_HANDLE_BAR, NULL);
gtk_widget_set_size_request (handle_bar, -1, CONTROL_HEIGHT);
gtk_box_pack_start (GTK_BOX (vbox3), handle_bar, FALSE, FALSE, 0);
gtk_widget_show (handle_bar);
gimp_handle_bar_connect_events (GIMP_HANDLE_BAR (handle_bar),
tool->input_bar);
gimp_handle_bar_connect_events (GIMP_HANDLE_BAR (handle_bar),
bar);
/* Horizontal box for input levels spinbuttons */
hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
gtk_widget_show (hbox);
/* low input spin */
hbox2 = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 2);
gtk_box_pack_start (GTK_BOX (hbox), hbox2, FALSE, FALSE, 0);
gtk_widget_show (hbox2);
button = gimp_levels_tool_color_picker_new (tool, PICK_LOW_INPUT);
gtk_box_pack_start (GTK_BOX (hbox2), button, FALSE, FALSE, 0);
gtk_widget_show (button);
tool->low_input_spinbutton = spinbutton =
gimp_prop_spin_button_new (filter_tool->config, "low-input",
0.01, 0.1, 1);
gtk_box_pack_start (GTK_BOX (hbox2), spinbutton, FALSE, FALSE, 0);
tool->low_input = gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON (spinbutton));
gimp_handle_bar_set_adjustment (GIMP_HANDLE_BAR (handle_bar), 0,
tool->low_input);
/* clamp input toggle */
hbox2 = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 2);
gtk_box_pack_start (GTK_BOX (hbox), hbox2, TRUE, FALSE, 0);
gtk_widget_show (hbox2);
button = gimp_prop_check_button_new (filter_tool->config, "clamp-input",
_("Clamp _input"));
gtk_box_pack_start (GTK_BOX (hbox2), button, FALSE, FALSE, 0);
/* input gamma spin */
spinbutton = gimp_prop_spin_button_new (filter_tool->config, "gamma",
0.01, 0.1, 2);
gtk_box_pack_start (GTK_BOX (hbox2), spinbutton, FALSE, FALSE, 0);
gimp_help_set_help_data (spinbutton, _("Gamma"), NULL);
tool->gamma = gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON (spinbutton));
tool->gamma_linear = gtk_adjustment_new (127, 0, 255, 0.1, 1.0, 0.0);
g_signal_connect (tool->gamma_linear, "value-changed",
G_CALLBACK (levels_linear_gamma_changed),
tool);
gimp_handle_bar_set_adjustment (GIMP_HANDLE_BAR (handle_bar), 1,
tool->gamma_linear);
g_object_unref (tool->gamma_linear);
/* high input spin */
hbox2 = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 2);
gtk_box_pack_end (GTK_BOX (hbox), hbox2, FALSE, FALSE, 0);
gtk_widget_show (hbox2);
button = gimp_levels_tool_color_picker_new (tool, PICK_HIGH_INPUT);
gtk_box_pack_start (GTK_BOX (hbox2), button, FALSE, FALSE, 0);
gtk_widget_show (button);
spinbutton = gimp_prop_spin_button_new (filter_tool->config, "high-input",
0.01, 0.1, 1);
gtk_box_pack_start (GTK_BOX (hbox2), spinbutton, FALSE, FALSE, 0);
tool->high_input_spinbutton = spinbutton;
tool->high_input = gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON (spinbutton));
gimp_handle_bar_set_adjustment (GIMP_HANDLE_BAR (handle_bar), 2,
tool->high_input);
/* Output levels frame */
frame = gimp_frame_new (_("Output Levels"));
gtk_box_pack_start (GTK_BOX (frame_vbox), frame, FALSE, FALSE, 0);
gtk_widget_show (frame);
vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 4);
gtk_container_add (GTK_CONTAINER (frame), vbox);
gtk_widget_show (vbox);
frame = gtk_frame_new (NULL);
gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
gtk_widget_show (frame);
vbox2 = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
gtk_container_set_border_width (GTK_CONTAINER (vbox2), border);
gtk_container_add (GTK_CONTAINER (frame), vbox2);
gtk_widget_show (vbox2);
tool->output_bar = g_object_new (GIMP_TYPE_COLOR_BAR, NULL);
gtk_widget_set_size_request (tool->output_bar, -1, GRADIENT_HEIGHT);
gtk_box_pack_start (GTK_BOX (vbox2), tool->output_bar, FALSE, FALSE, 0);
gtk_widget_show (tool->output_bar);
handle_bar = g_object_new (GIMP_TYPE_HANDLE_BAR, NULL);
gtk_widget_set_size_request (handle_bar, -1, CONTROL_HEIGHT);
gtk_box_pack_start (GTK_BOX (vbox2), handle_bar, FALSE, FALSE, 0);
gtk_widget_show (handle_bar);
gimp_handle_bar_connect_events (GIMP_HANDLE_BAR (handle_bar),
tool->output_bar);
/* Horizontal box for levels spin widgets */
hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
gtk_widget_show (hbox);
/* low output spin */
tool->low_output_spinbutton = spinbutton =
gimp_prop_spin_button_new (filter_tool->config, "low-output",
0.01, 0.1, 1);
gtk_box_pack_start (GTK_BOX (hbox), spinbutton, FALSE, FALSE, 0);
adjustment = gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON (spinbutton));
gimp_handle_bar_set_adjustment (GIMP_HANDLE_BAR (handle_bar), 0, adjustment);
/* clamp output toggle */
button = gimp_prop_check_button_new (filter_tool->config, "clamp-output",
_("Clamp outpu_t"));
gtk_box_pack_start (GTK_BOX (hbox), button, TRUE, FALSE, 0);
/* high output spin */
tool->high_output_spinbutton = spinbutton =
gimp_prop_spin_button_new (filter_tool->config, "high-output",
0.01, 0.1, 1);
gtk_box_pack_end (GTK_BOX (hbox), spinbutton, FALSE, FALSE, 0);
adjustment = gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON (spinbutton));
gimp_handle_bar_set_adjustment (GIMP_HANDLE_BAR (handle_bar), 2, adjustment);
/* all channels frame */
main_frame = gimp_frame_new (_("All Channels"));
gtk_box_pack_start (GTK_BOX (main_vbox), main_frame, FALSE, FALSE, 0);
gtk_widget_show (main_frame);
frame_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 4);
gtk_container_add (GTK_CONTAINER (main_frame), frame_vbox);
gtk_widget_show (frame_vbox);
hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
gtk_box_pack_start (GTK_BOX (frame_vbox), hbox, FALSE, FALSE, 0);
gtk_widget_show (hbox);
button = gtk_button_new_with_mnemonic (_("_Auto Input Levels"));
gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
gimp_help_set_help_data (button,
_("Adjust levels for all channels automatically"),
NULL);
gtk_widget_show (button);
g_signal_connect (button, "clicked",
G_CALLBACK (levels_stretch_callback),
tool);
button = gimp_levels_tool_color_picker_new (tool,
PICK_HIGH_INPUT |
PICK_ALL_CHANNELS);
gtk_box_pack_end (GTK_BOX (hbox), button, FALSE, FALSE, 0);
gtk_widget_show (button);
button = gimp_levels_tool_color_picker_new (tool,
PICK_GAMMA |
PICK_ALL_CHANNELS);
gtk_box_pack_end (GTK_BOX (hbox), button, FALSE, FALSE, 0);
gtk_widget_show (button);
button = gimp_levels_tool_color_picker_new (tool,
PICK_LOW_INPUT |
PICK_ALL_CHANNELS);
gtk_box_pack_end (GTK_BOX (hbox), button, FALSE, FALSE, 0);
gtk_widget_show (button);
button = gimp_icon_button_new (GIMP_ICON_TOOL_CURVES,
_("Edit these Settings as Curves"));
gtk_box_pack_start (GTK_BOX (main_vbox), button, FALSE, FALSE, 0);
gtk_widget_show (button);
g_signal_connect (button, "clicked",
G_CALLBACK (levels_to_curves_callback),
tool);
gimp_int_combo_box_set_active (GIMP_INT_COMBO_BOX (tool->channel_menu),
config->channel);
}
static void
gimp_levels_tool_reset (GimpFilterTool *filter_tool)
{
GimpHistogramChannel channel;
g_object_get (filter_tool->config,
"channel", &channel,
NULL);
GIMP_FILTER_TOOL_CLASS (parent_class)->reset (filter_tool);
g_object_set (filter_tool->config,
"channel", channel,
NULL);
}
static void
gimp_levels_tool_config_notify (GimpFilterTool *filter_tool,
GimpConfig *config,
const GParamSpec *pspec)
{
GimpLevelsTool *levels_tool = GIMP_LEVELS_TOOL (filter_tool);
GimpLevelsConfig *levels_config = GIMP_LEVELS_CONFIG (config);
GIMP_FILTER_TOOL_CLASS (parent_class)->config_notify (filter_tool,
config, pspec);
if (! levels_tool->channel_menu ||
! levels_tool->histogram_view)
return;
if (! strcmp (pspec->name, "trc"))
{
g_clear_object (&levels_tool->histogram);
g_clear_object (&levels_tool->histogram_async);
levels_tool->histogram = gimp_histogram_new (levels_config->trc);
levels_tool->histogram_async = gimp_drawable_calculate_histogram_async
(GIMP_TOOL (filter_tool)->drawables->data, levels_tool->histogram, FALSE);
gimp_histogram_view_set_histogram (GIMP_HISTOGRAM_VIEW (levels_tool->histogram_view),
levels_tool->histogram);
}
else if (! strcmp (pspec->name, "channel"))
{
gimp_histogram_view_set_channel (GIMP_HISTOGRAM_VIEW (levels_tool->histogram_view),
levels_config->channel);
gimp_color_bar_set_channel (GIMP_COLOR_BAR (levels_tool->output_bar),
levels_config->channel);
gimp_int_combo_box_set_active (GIMP_INT_COMBO_BOX (levels_tool->channel_menu),
levels_config->channel);
}
else if (! strcmp (pspec->name, "gamma") ||
! strcmp (pspec->name, "low-input") ||
! strcmp (pspec->name, "high-input"))
{
gdouble low = gtk_adjustment_get_value (levels_tool->low_input);
gdouble high = gtk_adjustment_get_value (levels_tool->high_input);
gdouble delta, mid, tmp, value;
gtk_adjustment_set_lower (levels_tool->high_input, low);
gtk_adjustment_set_lower (levels_tool->gamma_linear, low);
gtk_adjustment_set_upper (levels_tool->low_input, high);
gtk_adjustment_set_upper (levels_tool->gamma_linear, high);
levels_update_input_bar (levels_tool);
delta = (high - low) / 2.0;
mid = low + delta;
tmp = log10 (1.0 / levels_config->gamma[levels_config->channel]);
value = mid + delta * tmp;
gtk_adjustment_set_value (levels_tool->gamma_linear, value);
}
}
static gboolean
gimp_levels_tool_settings_import (GimpFilterTool *filter_tool,
GInputStream *input,
GError **error)
{
GimpLevelsConfig *config = GIMP_LEVELS_CONFIG (filter_tool->config);
gchar header[64];
gsize bytes_read;
if (! g_input_stream_read_all (input, header, sizeof (header),
&bytes_read, NULL, error) ||
bytes_read != sizeof (header))
{
if (error && *error)
g_prefix_error (error, _("Could not read header: "));
else
g_set_error (error, GIMP_DATA_ERROR, GIMP_DATA_ERROR_READ,
_("Could not read header: "));
return FALSE;
}
g_seekable_seek (G_SEEKABLE (input), 0, G_SEEK_SET, NULL, NULL);
if (g_str_has_prefix (header, "# GIMP Levels File\n"))
return gimp_levels_config_load_cruft (config, input, error);
/* Check if we're loading a Photoshop Levels preset .alv file */
if (bytes_read > 2 && header[0] == 0 && (header[1] == 2))
return gimp_levels_config_load_alv (config, input, error);
return GIMP_FILTER_TOOL_CLASS (parent_class)->settings_import (filter_tool,
input,
error);
}
static gboolean
gimp_levels_tool_settings_export (GimpFilterTool *filter_tool,
GOutputStream *output,
GError **error)
{
GimpLevelsTool *tool = GIMP_LEVELS_TOOL (filter_tool);
GimpLevelsConfig *config = GIMP_LEVELS_CONFIG (filter_tool->config);
if (tool->export_old_format)
return gimp_levels_config_save_cruft (config, output, error);
return GIMP_FILTER_TOOL_CLASS (parent_class)->settings_export (filter_tool,
output,
error);
}
static void
levels_input_adjust_by_color (GimpLevelsConfig *config,
guint value,
GimpHistogramChannel channel,
const Babl *target_space,
GeglColor *color)
{
switch (value & 0xF)
{
case PICK_LOW_INPUT:
gimp_levels_config_adjust_by_colors (config, channel, target_space, color, NULL, NULL);
break;
case PICK_GAMMA:
gimp_levels_config_adjust_by_colors (config, channel, target_space, NULL, color, NULL);
break;
case PICK_HIGH_INPUT:
gimp_levels_config_adjust_by_colors (config, channel, target_space, NULL, NULL, color);
break;
default:
break;
}
}
static void
gimp_levels_tool_color_picked (GimpFilterTool *color_tool,
gpointer identifier,
gdouble x,
gdouble y,
const Babl *sample_format,
GeglColor *color)
{
GimpFilterTool *filter_tool = GIMP_FILTER_TOOL (color_tool);
GimpLevelsConfig *config = GIMP_LEVELS_CONFIG (filter_tool->config);
guint value = GPOINTER_TO_UINT (identifier);
const Babl *target_space = NULL;
target_space = gimp_drawable_get_space (GIMP_TOOL (filter_tool)->drawables->data);
if (value & PICK_ALL_CHANNELS &&
gimp_babl_format_get_base_type (sample_format) == GIMP_RGB)
{
GimpHistogramChannel channel;
/* first reset the value channel */
switch (value & 0xF)
{
case PICK_LOW_INPUT:
config->low_input[GIMP_HISTOGRAM_VALUE] = 0.0;
break;
case PICK_GAMMA:
config->gamma[GIMP_HISTOGRAM_VALUE] = 1.0;
break;
case PICK_HIGH_INPUT:
config->high_input[GIMP_HISTOGRAM_VALUE] = 1.0;
break;
default:
break;
}
/* then adjust all color channels */
for (channel = GIMP_HISTOGRAM_RED;
channel <= GIMP_HISTOGRAM_BLUE;
channel++)
{
levels_input_adjust_by_color (config, value, channel, target_space, color);
}
}
else
{
levels_input_adjust_by_color (config, value, config->channel, target_space, color);
}
}
static void
gimp_levels_tool_export_setup (GimpSettingsBox *settings_box,
GtkFileChooserDialog *dialog,
gboolean export,
GimpLevelsTool *tool)
{
GtkWidget *button;
if (! export)
return;
button = gtk_check_button_new_with_mnemonic (_("Use _old levels file format"));
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button),
tool->export_old_format);
gtk_file_chooser_set_extra_widget (GTK_FILE_CHOOSER (dialog), button);
gtk_widget_show (button);
g_signal_connect (button, "toggled",
G_CALLBACK (gimp_toggle_button_update),
&tool->export_old_format);
}
static void
levels_update_input_bar (GimpLevelsTool *tool)
{
GimpFilterTool *filter_tool = GIMP_FILTER_TOOL (tool);
GimpLevelsConfig *config = GIMP_LEVELS_CONFIG (filter_tool->config);
switch (config->channel)
{
gdouble value;
case GIMP_HISTOGRAM_VALUE:
case GIMP_HISTOGRAM_ALPHA:
case GIMP_HISTOGRAM_RGB:
case GIMP_HISTOGRAM_LUMINANCE:
{
guchar v[256];
gint i;
for (i = 0; i < 256; i++)
{
value = gimp_operation_levels_map_input (config,
config->channel,
i / 255.0);
v[i] = CLAMP (value, 0.0, 1.0) * 255.999;
}
gimp_color_bar_set_buffers (GIMP_COLOR_BAR (tool->input_bar),
v, v, v);
}
break;
case GIMP_HISTOGRAM_RED:
case GIMP_HISTOGRAM_GREEN:
case GIMP_HISTOGRAM_BLUE:
{
guchar r[256];
guchar g[256];
guchar b[256];
gint i;
for (i = 0; i < 256; i++)
{
value = gimp_operation_levels_map_input (config,
GIMP_HISTOGRAM_RED,
i / 255.0);
r[i] = CLAMP (value, 0.0, 1.0) * 255.999;
value = gimp_operation_levels_map_input (config,
GIMP_HISTOGRAM_GREEN,
i / 255.0);
g[i] = CLAMP (value, 0.0, 1.0) * 255.999;
value = gimp_operation_levels_map_input (config,
GIMP_HISTOGRAM_BLUE,
i / 255.0);
b[i] = CLAMP (value, 0.0, 1.0) * 255.999;
}
gimp_color_bar_set_buffers (GIMP_COLOR_BAR (tool->input_bar),
r, g, b);
}
break;
}
}
static void
levels_channel_callback (GtkWidget *widget,
GimpFilterTool *filter_tool)
{
GimpLevelsConfig *config = GIMP_LEVELS_CONFIG (filter_tool->config);
gint value;
if (gimp_int_combo_box_get_active (GIMP_INT_COMBO_BOX (widget), &value) &&
config->channel != value)
{
g_object_set (config,
"channel", value,
NULL);
}
}
static void
levels_channel_reset_callback (GtkWidget *widget,
GimpFilterTool *filter_tool)
{
gimp_levels_config_reset_channel (GIMP_LEVELS_CONFIG (filter_tool->config));
}
static gboolean
levels_menu_sensitivity (gint value,
gpointer data)
{
GimpDrawable *drawable;
GimpHistogramChannel channel = value;
if (! GIMP_TOOL (data)->drawables)
return FALSE;
drawable = GIMP_TOOL (data)->drawables->data;
switch (channel)
{
case GIMP_HISTOGRAM_VALUE:
return TRUE;
case GIMP_HISTOGRAM_RED:
case GIMP_HISTOGRAM_GREEN:
case GIMP_HISTOGRAM_BLUE:
return gimp_drawable_is_rgb (drawable);
case GIMP_HISTOGRAM_ALPHA:
return gimp_drawable_has_alpha (drawable);
case GIMP_HISTOGRAM_RGB:
return FALSE;
case GIMP_HISTOGRAM_LUMINANCE:
return FALSE;
}
return FALSE;
}
static void
levels_stretch_callback (GtkWidget *widget,
GimpLevelsTool *levels_tool)
{
GimpTool *tool = GIMP_TOOL (levels_tool);
GimpFilterTool *filter_tool = GIMP_FILTER_TOOL (levels_tool);
GimpWaitable *waitable;
waitable = gimp_trivially_cancelable_waitable_new (
GIMP_WAITABLE (levels_tool->histogram_async));
gimp_wait (tool->tool_info->gimp, waitable, _("Calculating histogram..."));
g_object_unref (waitable);
if (gimp_async_is_synced (levels_tool->histogram_async) &&
gimp_async_is_finished (levels_tool->histogram_async))
{
gimp_levels_config_stretch (GIMP_LEVELS_CONFIG (filter_tool->config),
levels_tool->histogram,
gimp_drawable_is_rgb (tool->drawables->data));
}
}
static void
levels_linear_gamma_changed (GtkAdjustment *adjustment,
GimpLevelsTool *tool)
{
gdouble low_input = gtk_adjustment_get_value (tool->low_input);
gdouble high_input = gtk_adjustment_get_value (tool->high_input);
gdouble delta, mid, tmp, value;
delta = (high_input - low_input) / 2.0;
if (delta >= 0.5)
{
mid = low_input + delta;
tmp = (gtk_adjustment_get_value (adjustment) - mid) / delta;
value = 1.0 / pow (10, tmp);
/* round the gamma value to the nearest 1/100th */
value = floor (value * 100 + 0.5) / 100.0;
gtk_adjustment_set_value (tool->gamma, value);
}
}
static void
levels_to_curves_callback (GtkWidget *widget,
GimpFilterTool *filter_tool)
{
GimpLevelsConfig *config = GIMP_LEVELS_CONFIG (filter_tool->config);
GimpCurvesConfig *curves;
curves = gimp_levels_config_to_curves_config (config);
gimp_filter_tool_edit_as (filter_tool,
"gimp-curves-tool",
GIMP_CONFIG (curves));
g_object_unref (curves);
}