1 /* GIMP - The GNU Image Manipulation Program
2 * Copyright (C) 1995 Spencer Kimball and Peter Mattis
3 *
4 * gimpoperationlevels.c
5 * Copyright (C) 2007 Michael Natterer <mitch@gimp.org>
6 *
7 * This program is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <https://www.gnu.org/licenses/>.
19 */
20
21 #include "config.h"
22
23 #include <cairo.h>
24 #include <gdk-pixbuf/gdk-pixbuf.h>
25 #include <gegl.h>
26
27 #include "libgimpmath/gimpmath.h"
28
29 #include "operations-types.h"
30
31 #include "gimplevelsconfig.h"
32 #include "gimpoperationlevels.h"
33
34 #include "gimp-intl.h"
35
36
37 static gboolean gimp_operation_levels_process (GeglOperation *operation,
38 void *in_buf,
39 void *out_buf,
40 glong samples,
41 const GeglRectangle *roi,
42 gint level);
43
44
G_DEFINE_TYPE(GimpOperationLevels,gimp_operation_levels,GIMP_TYPE_OPERATION_POINT_FILTER)45 G_DEFINE_TYPE (GimpOperationLevels, gimp_operation_levels,
46 GIMP_TYPE_OPERATION_POINT_FILTER)
47
48 #define parent_class gimp_operation_levels_parent_class
49
50
51 static void
52 gimp_operation_levels_class_init (GimpOperationLevelsClass *klass)
53 {
54 GObjectClass *object_class = G_OBJECT_CLASS (klass);
55 GeglOperationClass *operation_class = GEGL_OPERATION_CLASS (klass);
56 GeglOperationPointFilterClass *point_class = GEGL_OPERATION_POINT_FILTER_CLASS (klass);
57
58 object_class->set_property = gimp_operation_point_filter_set_property;
59 object_class->get_property = gimp_operation_point_filter_get_property;
60
61 gegl_operation_class_set_keys (operation_class,
62 "name", "gimp:levels",
63 "categories", "color",
64 "description", _("Adjust color levels"),
65 NULL);
66
67 point_class->process = gimp_operation_levels_process;
68
69 g_object_class_install_property (object_class,
70 GIMP_OPERATION_POINT_FILTER_PROP_LINEAR,
71 g_param_spec_boolean ("linear",
72 "Linear",
73 "Whether to operate on linear RGB",
74 FALSE,
75 G_PARAM_READWRITE));
76
77 g_object_class_install_property (object_class,
78 GIMP_OPERATION_POINT_FILTER_PROP_CONFIG,
79 g_param_spec_object ("config",
80 "Config",
81 "The config object",
82 GIMP_TYPE_LEVELS_CONFIG,
83 G_PARAM_READWRITE |
84 G_PARAM_CONSTRUCT));
85 }
86
87 static void
gimp_operation_levels_init(GimpOperationLevels * self)88 gimp_operation_levels_init (GimpOperationLevels *self)
89 {
90 }
91
92 static inline gdouble
gimp_operation_levels_map(gdouble value,gdouble low_input,gdouble high_input,gboolean clamp_input,gdouble inv_gamma,gdouble low_output,gdouble high_output,gboolean clamp_output)93 gimp_operation_levels_map (gdouble value,
94 gdouble low_input,
95 gdouble high_input,
96 gboolean clamp_input,
97 gdouble inv_gamma,
98 gdouble low_output,
99 gdouble high_output,
100 gboolean clamp_output)
101 {
102 /* determine input intensity */
103 if (high_input != low_input)
104 value = (value - low_input) / (high_input - low_input);
105 else
106 value = (value - low_input);
107
108 if (clamp_input)
109 value = CLAMP (value, 0.0, 1.0);
110
111 if (inv_gamma != 1.0 && value > 0)
112 value = pow (value, inv_gamma);
113
114 /* determine the output intensity */
115 if (high_output >= low_output)
116 value = value * (high_output - low_output) + low_output;
117 else if (high_output < low_output)
118 value = low_output - value * (low_output - high_output);
119
120 if (clamp_output)
121 value = CLAMP (value, 0.0, 1.0);
122
123 return value;
124 }
125
126 static gboolean
gimp_operation_levels_process(GeglOperation * operation,void * in_buf,void * out_buf,glong samples,const GeglRectangle * roi,gint level)127 gimp_operation_levels_process (GeglOperation *operation,
128 void *in_buf,
129 void *out_buf,
130 glong samples,
131 const GeglRectangle *roi,
132 gint level)
133 {
134 GimpOperationPointFilter *point = GIMP_OPERATION_POINT_FILTER (operation);
135 GimpLevelsConfig *config = GIMP_LEVELS_CONFIG (point->config);
136 gfloat *src = in_buf;
137 gfloat *dest = out_buf;
138 gfloat inv_gamma[5];
139 gint channel;
140
141 if (! config)
142 return FALSE;
143
144 for (channel = 0; channel < 5; channel++)
145 {
146 g_return_val_if_fail (config->gamma[channel] != 0.0, FALSE);
147
148 inv_gamma[channel] = 1.0 / config->gamma[channel];
149 }
150
151 while (samples--)
152 {
153 for (channel = 0; channel < 4; channel++)
154 {
155 gdouble value;
156
157 value = gimp_operation_levels_map (src[channel],
158 config->low_input[channel + 1],
159 config->high_input[channel + 1],
160 config->clamp_input,
161 inv_gamma[channel + 1],
162 config->low_output[channel + 1],
163 config->high_output[channel + 1],
164 config->clamp_output);
165
166 /* don't apply the overall curve to the alpha channel */
167 if (channel != ALPHA)
168 value = gimp_operation_levels_map (value,
169 config->low_input[0],
170 config->high_input[0],
171 config->clamp_input,
172 inv_gamma[0],
173 config->low_output[0],
174 config->high_output[0],
175 config->clamp_output);
176
177 dest[channel] = value;
178 }
179
180 src += 4;
181 dest += 4;
182 }
183
184 return TRUE;
185 }
186
187
188 /* public functions */
189
190 gdouble
gimp_operation_levels_map_input(GimpLevelsConfig * config,GimpHistogramChannel channel,gdouble value)191 gimp_operation_levels_map_input (GimpLevelsConfig *config,
192 GimpHistogramChannel channel,
193 gdouble value)
194 {
195 g_return_val_if_fail (GIMP_IS_LEVELS_CONFIG (config), 0.0);
196
197 /* determine input intensity */
198 if (config->high_input[channel] != config->low_input[channel])
199 value = ((value - config->low_input[channel]) /
200 (config->high_input[channel] - config->low_input[channel]));
201 else
202 value = (value - config->low_input[channel]);
203
204 if (config->gamma[channel] != 0.0 && value > 0.0)
205 value = pow (value, 1.0 / config->gamma[channel]);
206
207 return value;
208 }
209