channel-mixer.c 7.12 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
/* This file is an image processing operation for GEGL
 *
 * GEGL is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 3 of the License, or (at your option) any later version.
 *
 * GEGL 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with GEGL; if not, see <http://www.gnu.org/licenses/>.
 *
 * This operation is a port of the GIMP channel-mixer plugin:
 *
 *     Copyright (C) 2002 Martin Guldahl <mguldahl@xmission.com>
 *
 * The porting to GEGL was done by Barak Itkin
 *
 *     Copyright (C) 2013 Barak Itkin <lightningismyname@gmail.com>
 */

#include "config.h"
#include <glib/gi18n-lib.h>
#include <math.h>

29
#ifdef GEGL_PROPERTIES
30

31
property_boolean (preserve_luminosity, _("Preserve luminosity"), FALSE)
32

33 34
/* Red channel */
property_double (rr_gain, _("Red in Red channel"), 1.0)
35
  description(_("Set the red amount for the red channel"))
36
  value_range (-2.0, 2.0)
37

38
property_double (rg_gain, _("Green in Red channel"), 0.0)
39
  description(_("Set the green amount for the red channel"))
40
  value_range (-2.0, 2.0)
41

42
property_double (rb_gain, _("Blue in Red channel"), 0.0)
43
  description(_("Set the blue amount for the red channel"))
44
  value_range (-2.0, 2.0)
45

46 47 48
/* Green channel */
property_double (gr_gain, _("Red in Green channel"), 0.0)
  description(_("Set the red amount for the green channel"))
49
  value_range (-2.0, 2.0)
50

51 52
property_double (gg_gain, _("Green for Green channel"), 1.0)
  description(_("Set the green amount for the green channel"))
53
  value_range (-2.0, 2.0)
54

55
property_double (gb_gain, _("Blue in Green channel"), 0.0)
56 57
  description(_("Set the blue amount for the green channel"))
  value_range (-2.0, 2.0)
58

59 60 61
/* Blue channel */
property_double (br_gain, _("Red in Blue channel"), 0.0)
  description(_("Set the red amount for the blue channel"))
62 63
  value_range (-2.0, 2.0)

64 65
property_double (bg_gain, _("Green in Blue channel"), 0.0)
  description(_("Set the green amount for the blue channel"))
66 67
  value_range (-2.0, 2.0)

68 69
property_double (bb_gain, _("Blue in Blue channel"), 1.0)
  description(_("Set the blue amount for the blue channel"))
70
  value_range (-2.0, 2.0)
71 72 73 74


#else

75
#define GEGL_OP_POINT_FILTER
76
#define GEGL_OP_NAME     channel_mixer
77
#define GEGL_OP_C_SOURCE channel-mixer.c
78

79
#include "gegl-op.h"
80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107

typedef enum
{
  CM_RED_CHANNEL,
  CM_GREEN_CHANNEL,
  CM_BLUE_CHANNEL
} CmModeType;

typedef struct
{
  gdouble       red_gain;
  gdouble       green_gain;
  gdouble       blue_gain;
} CmChannelType;

typedef struct
{
  CmChannelType  red;
  CmChannelType  green;
  CmChannelType  blue;

  gboolean       preserve_luminosity;
  gboolean       has_alpha;
} CmParamsType;

static void prepare (GeglOperation *operation)
{
  const Babl *input_format = gegl_operation_get_source_format (operation, "input");
108
  GeglProperties *o = GEGL_PROPERTIES (operation);
109 110 111
  CmParamsType *mix;
  const Babl *format;

112 113
  if (o->user_data == NULL)
    o->user_data = g_slice_new0 (CmParamsType);
114

115
  mix = (CmParamsType*) o->user_data;
116 117 118

  mix->preserve_luminosity = o->preserve_luminosity;

119 120 121
  mix->red.red_gain     = o->rr_gain;
  mix->red.green_gain   = o->rg_gain;
  mix->red.blue_gain    = o->rb_gain;
122

123 124 125
  mix->green.red_gain   = o->gr_gain;
  mix->green.green_gain = o->gg_gain;
  mix->green.blue_gain  = o->gb_gain;
126

127 128 129
  mix->blue.red_gain    = o->br_gain;
  mix->blue.green_gain  = o->bg_gain;
  mix->blue.blue_gain   = o->bb_gain;
130 131 132 133

  if (input_format == NULL || babl_format_has_alpha (input_format))
    {
      mix->has_alpha = TRUE;
134
      format = babl_format ("RGBA float");
135 136 137 138
    }
  else
    {
      mix->has_alpha = FALSE;
139
      format = babl_format ("RGB float");
140 141 142 143 144 145 146 147 148
    }

  gegl_operation_set_format (operation, "input", format);
  gegl_operation_set_format (operation, "output", format);
}

static void finalize (GObject *object)
{
  GeglOperation *op = (void*) object;
149
  GeglProperties *o = GEGL_PROPERTIES (op);
150

151
  if (o->user_data)
152
    {
153 154
      g_slice_free (CmParamsType, o->user_data);
      o->user_data = NULL;
155 156
    }

157
  G_OBJECT_CLASS (gegl_op_parent_class)->finalize (object);
158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182
}

static gdouble
cm_calculate_norm (CmParamsType  *mix,
                   CmChannelType *ch)
{
  gdouble sum = ch->red_gain + ch->green_gain + ch->blue_gain;

  if (sum == 0.0 || ! mix->preserve_luminosity)
    return 1.0;

  return fabs (1 / sum);
}

static inline gfloat
cm_mix_pixel (CmChannelType *ch,
              gfloat         r,
              gfloat         g,
              gfloat         b,
              gdouble        norm)
{
  gdouble c = ch->red_gain * r + ch->green_gain * g + ch->blue_gain * b;

  c *= norm;

183
  return (gfloat) c;
184 185 186 187 188 189 190 191
}

static inline void
cm_process_pixel (CmParamsType  *mix,
                  const gfloat  *s,
                  gfloat        *d,
                  const gdouble  red_norm,
                  const gdouble  green_norm,
192
                  const gdouble  blue_norm)
193
{
194 195 196 197

  d[0] = cm_mix_pixel (&mix->red,   s[0], s[1], s[2], red_norm);
  d[1] = cm_mix_pixel (&mix->green, s[0], s[1], s[2], green_norm);
  d[2] = cm_mix_pixel (&mix->blue,  s[0], s[1], s[2], blue_norm);
198 199 200 201 202 203 204 205 206 207
}

static gboolean
process (GeglOperation       *op,
         void                *in_buf,
         void                *out_buf,
         glong                samples,
         const GeglRectangle *roi,
         gint                 level)
{
208 209
  GeglProperties   *o = GEGL_PROPERTIES (op);
  CmParamsType *mix = (CmParamsType*) o->user_data;
210

211
  gdouble       red_norm, green_norm, blue_norm;
212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227
  gfloat       *in, *out;

  g_assert (mix != NULL);

  red_norm   = cm_calculate_norm (mix, &mix->red);
  green_norm = cm_calculate_norm (mix, &mix->green);
  blue_norm  = cm_calculate_norm (mix, &mix->blue);

  in = in_buf;
  out = out_buf;

  if (mix->has_alpha)
    {
      while (samples--)
        {
          cm_process_pixel (mix, in, out,
228
                            red_norm, green_norm, blue_norm);
229 230 231 232 233 234 235 236 237 238 239
          out[3] = in[3];

          in += 4;
          out += 4;
        }
    }
  else
    {
      while (samples--)
        {
          cm_process_pixel (mix, in, out,
240
                            red_norm, green_norm, blue_norm);
241 242 243 244 245 246 247 248 249
          in += 3;
          out += 3;
        }
    }

  return TRUE;
}

static void
250
gegl_op_class_init (GeglOpClass *klass)
251 252 253 254 255 256 257 258 259 260 261 262 263 264
{
  GeglOperationClass            *operation_class;
  GeglOperationPointFilterClass *point_filter_class;

  operation_class = GEGL_OPERATION_CLASS (klass);
  point_filter_class = GEGL_OPERATION_POINT_FILTER_CLASS (klass);

  point_filter_class->process = process;
  operation_class->prepare = prepare;
  G_OBJECT_CLASS (klass)->finalize = finalize;

  operation_class->opencl_support = TRUE;

  gegl_operation_class_set_keys (operation_class,
265 266 267 268 269
      "name",       "gegl:channel-mixer",
      "categories", "color",
      "title",      _("Channel Mixer"),
      "license",    "GPL3+",
      "description", _("Remix colors; by defining relative contributions from source components."),
270 271 272 273 274
      NULL);
}

#endif