gimpoperationlevels.c 7.54 KB
Newer Older
1 2 3 4 5 6
/* GIMP - The GNU Image Manipulation Program
 * Copyright (C) 1995 Spencer Kimball and Peter Mattis
 *
 * gimpoperationlevels.c
 * Copyright (C) 2007 Michael Natterer <mitch@gimp.org>
 *
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 11 12 13 14 15 16 17
 * (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
18
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
19 20 21 22
 */

#include "config.h"

23
#include <cairo.h>
24
#include <gdk-pixbuf/gdk-pixbuf.h>
25
#include <gegl.h>
26 27 28

#include "libgimpmath/gimpmath.h"

29
#include "operations-types.h"
30

31
#include "gimplevelsconfig.h"
32 33
#include "gimpoperationlevels.h"

34 35
#include "gimp-intl.h"

36

37 38 39 40
static gboolean gimp_operation_levels_process (GeglOperation       *operation,
                                               void                *in_buf,
                                               void                *out_buf,
                                               glong                samples,
41 42
                                               const GeglRectangle *roi,
                                               gint                 level);
43 44 45


G_DEFINE_TYPE (GimpOperationLevels, gimp_operation_levels,
46
               GIMP_TYPE_OPERATION_POINT_FILTER)
47 48 49 50 51

#define parent_class gimp_operation_levels_parent_class


static void
52
gimp_operation_levels_class_init (GimpOperationLevelsClass *klass)
53 54 55 56 57
{
  GObjectClass                  *object_class    = G_OBJECT_CLASS (klass);
  GeglOperationClass            *operation_class = GEGL_OPERATION_CLASS (klass);
  GeglOperationPointFilterClass *point_class     = GEGL_OPERATION_POINT_FILTER_CLASS (klass);

58 59
  object_class->set_property   = gimp_operation_point_filter_set_property;
  object_class->get_property   = gimp_operation_point_filter_get_property;
60

61
  gegl_operation_class_set_keys (operation_class,
62 63
                                 "name",        "gimp:levels",
                                 "categories",  "color",
64
                                 "description", _("Adjust color levels"),
65
                                 NULL);
66

67
  point_class->process = gimp_operation_levels_process;
68

69 70 71 72 73 74 75 76
  g_object_class_install_property (object_class,
                                   GIMP_OPERATION_POINT_FILTER_PROP_LINEAR,
                                   g_param_spec_boolean ("linear",
                                                         "Linear",
                                                         "Whether to operate on linear RGB",
                                                         FALSE,
                                                         G_PARAM_READWRITE));

77 78
  g_object_class_install_property (object_class,
                                   GIMP_OPERATION_POINT_FILTER_PROP_CONFIG,
79 80 81 82
                                   g_param_spec_object ("config",
                                                        "Config",
                                                        "The config object",
                                                        GIMP_TYPE_LEVELS_CONFIG,
83 84
                                                        G_PARAM_READWRITE |
                                                        G_PARAM_CONSTRUCT));
85 86 87 88 89
}

static void
gimp_operation_levels_init (GimpOperationLevels *self)
{
90
}
91

92
static inline gdouble
93 94 95 96 97 98 99 100
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)
101 102 103
{
  /*  determine input intensity  */
  if (high_input != low_input)
104
    value = (value - low_input) / (high_input - low_input);
105
  else
106
    value = (value - low_input);
107

108 109 110
  if (clamp_input)
    value = CLAMP (value, 0.0, 1.0);

111
  if (inv_gamma != 1.0 && value > 0)
112
    value =  pow (value, inv_gamma);
113 114 115 116 117 118 119

  /*  determine the output intensity  */
  if (high_output >= low_output)
    value = value * (high_output - low_output) + low_output;
  else if (high_output < low_output)
    value = low_output - value * (low_output - high_output);

120 121 122
  if (clamp_output)
    value = CLAMP (value, 0.0, 1.0);

123 124 125 126
  return value;
}

static gboolean
127 128 129 130
gimp_operation_levels_process (GeglOperation       *operation,
                               void                *in_buf,
                               void                *out_buf,
                               glong                samples,
131 132
                               const GeglRectangle *roi,
                               gint                 level)
133
{
134 135 136 137
  GimpOperationPointFilter *point  = GIMP_OPERATION_POINT_FILTER (operation);
  GimpLevelsConfig         *config = GIMP_LEVELS_CONFIG (point->config);
  gfloat                   *src    = in_buf;
  gfloat                   *dest   = out_buf;
138 139
  gfloat                    inv_gamma[5];
  gint                      channel;
140

141 142 143
  if (! config)
    return FALSE;

144
  for (channel = 0; channel < 5; channel++)
145
    {
146 147 148 149
      g_return_val_if_fail (config->gamma[channel] != 0.0, FALSE);

      inv_gamma[channel] = 1.0 / config->gamma[channel];
    }
150

151 152
  while (samples--)
    {
153 154
      for (channel = 0; channel < 4; channel++)
        {
155
          gdouble value;
156 157

          value = gimp_operation_levels_map (src[channel],
158 159
                                             config->low_input[channel + 1],
                                             config->high_input[channel + 1],
160 161
                                             config->clamp_input,
                                             inv_gamma[channel + 1],
162
                                             config->low_output[channel + 1],
163 164
                                             config->high_output[channel + 1],
                                             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 170
                                               config->low_input[0],
                                               config->high_input[0],
171 172
                                               config->clamp_input,
                                               inv_gamma[0],
173
                                               config->low_output[0],
174 175
                                               config->high_output[0],
                                               config->clamp_output);
176 177 178 179 180 181 182 183 184 185

          dest[channel] = value;
        }

      src  += 4;
      dest += 4;
    }

  return TRUE;
}
186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204


/*  public functions  */

gdouble
gimp_operation_levels_map_input (GimpLevelsConfig     *config,
                                 GimpHistogramChannel  channel,
                                 gdouble               value)
{
  g_return_val_if_fail (GIMP_IS_LEVELS_CONFIG (config), 0.0);

  /*  determine input intensity  */
  if (config->high_input[channel] != config->low_input[channel])
    value = ((value - config->low_input[channel]) /
             (config->high_input[channel] - config->low_input[channel]));
  else
    value = (value - config->low_input[channel]);

  if (config->gamma[channel] != 0.0)
205
    value = pow (value, 1.0 / config->gamma[channel]);
206 207 208

  return value;
}