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