softglow.c 6.98 KB
Newer Older
Maxime Nicco's avatar
Maxime Nicco committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
/* 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/>.
 *
 * Copyright 1997 Spencer Kimball
 * Copyright 2012 Maxime Nicco <maxime.nicco@gmail.com>
18
 * Copyright 2013 Téo Mazars <teo.mazars@ensimag.fr>
Maxime Nicco's avatar
Maxime Nicco committed
19 20 21 22 23
 */

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

24
#ifdef GEGL_PROPERTIES
Maxime Nicco's avatar
Maxime Nicco committed
25

26 27 28
property_double (glow_radius, _("Glow radius"), 10.0)
    value_range (1.0, 50.0)
    ui_meta    ("unit", "pixel-distance")
Maxime Nicco's avatar
Maxime Nicco committed
29

30 31
property_double (brightness, _("Brightness"), 0.30)
    value_range (0.0, 1.0)
Maxime Nicco's avatar
Maxime Nicco committed
32

33 34
property_double (sharpness, _("Sharpness"), 0.85)
    value_range (0.0, 1.0)
Maxime Nicco's avatar
Maxime Nicco committed
35 36 37

#else

38 39
#define GEGL_OP_AREA_FILTER
#define GEGL_OP_C_FILE "softglow.c"
Maxime Nicco's avatar
Maxime Nicco committed
40

41
#include "gegl-op.h"
Maxime Nicco's avatar
Maxime Nicco committed
42 43 44 45 46
#include <stdio.h>
#include <math.h>
#include <stdlib.h>

#define SIGMOIDAL_BASE   2
47
#define SIGMOIDAL_RANGE  20
Maxime Nicco's avatar
Maxime Nicco committed
48

49
static GeglBuffer *
50 51 52
grey_blur_buffer (GeglBuffer          *input,
                  gdouble              glow_radius,
                  const GeglRectangle *result)
Maxime Nicco's avatar
Maxime Nicco committed
53
{
54
  GeglNode *gegl, *image, *write, *blur, *crop;
55
  GeglBuffer *dest;
Maxime Nicco's avatar
Maxime Nicco committed
56 57
  gdouble radius, std_dev;

58 59 60 61 62
  gegl = gegl_node_new ();
  image = gegl_node_new_child (gegl,
                "operation", "gegl:buffer-source",
                "buffer", input,
                NULL);
Maxime Nicco's avatar
Maxime Nicco committed
63 64 65

  radius   = fabs (glow_radius) + 1.0;
  std_dev = sqrt (-(radius * radius) / (2 * log (1.0 / 255.0)));
66

67
  blur =  gegl_node_new_child (gegl,
68 69 70 71 72
                "operation", "gegl:gaussian-blur",
                "std_dev_x", std_dev,
                "std_dev_y", std_dev,
                NULL);

73 74 75 76 77 78 79 80
  crop =  gegl_node_new_child (gegl,
                "operation", "gegl:crop",
                "x",     (gdouble) result->x,
                "y",     (gdouble) result->y,
                "width", (gdouble) result->width,
                "height",(gdouble) result->height,
                NULL);

81
  write = gegl_node_new_child (gegl,
82
                "operation", "gegl:buffer-sink",
83
                "buffer", &dest, NULL);
84

85
  gegl_node_link_many (image, blur, crop, write, NULL);
Maxime Nicco's avatar
Maxime Nicco committed
86 87
  gegl_node_process (write);

88 89 90
  g_object_unref (gegl);

  return dest;
Maxime Nicco's avatar
Maxime Nicco committed
91 92
}

93 94
static void
prepare (GeglOperation *operation)
95
{
96
  GeglOperationAreaFilter *area = GEGL_OPERATION_AREA_FILTER (operation);
97
  GeglProperties          *o    = GEGL_PROPERTIES (operation);
98 99 100 101

  area->left = area->right = ceil (fabs (o->glow_radius)) +1;
  area->top = area->bottom = ceil (fabs (o->glow_radius)) +1;

Maxime Nicco's avatar
Maxime Nicco committed
102
  gegl_operation_set_format (operation, "input",
103
                             babl_format ("RGBA float"));
Maxime Nicco's avatar
Maxime Nicco committed
104 105 106 107
  gegl_operation_set_format (operation, "output",
                             babl_format ("RGBA float"));
}

108 109 110 111 112 113 114 115 116 117 118 119 120
static GeglRectangle
get_bounding_box (GeglOperation *operation)
{
  GeglRectangle *region;

  region = gegl_operation_source_get_bounding_box (operation, "input");

  if (region != NULL)
    return *region;
  else
    return *GEGL_RECTANGLE (0, 0, 0, 0);
}

Maxime Nicco's avatar
Maxime Nicco committed
121 122 123 124 125 126 127
static gboolean
process (GeglOperation       *operation,
         GeglBuffer          *input,
         GeglBuffer          *output,
         const GeglRectangle *result,
         gint                 level)
{
128
  GeglOperationAreaFilter *area = GEGL_OPERATION_AREA_FILTER (operation);
129
  GeglProperties              *o    = GEGL_PROPERTIES (operation);
Maxime Nicco's avatar
Maxime Nicco committed
130 131 132

  GeglBuffer *dest, *dest_tmp;

133 134
  GeglRectangle  working_region;
  GeglRectangle *whole_region;
135 136

  GeglBufferIterator *iter;
Maxime Nicco's avatar
Maxime Nicco committed
137 138 139

  whole_region = gegl_operation_source_get_bounding_box (operation, "input");

140 141 142 143 144 145 146
  working_region.x      = result->x - area->left;
  working_region.width  = result->width + area->left + area->right;
  working_region.y      = result->y - area->top;
  working_region.height = result->height + area->top + area->bottom;

  gegl_rectangle_intersect (&working_region, &working_region, whole_region);

147
  dest_tmp = gegl_buffer_new (&working_region, babl_format ("Y' float"));
Maxime Nicco's avatar
Maxime Nicco committed
148

149
  iter = gegl_buffer_iterator_new (dest_tmp, &working_region, 0, babl_format ("Y' float"),
150
                                   GEGL_ACCESS_WRITE, GEGL_ABYSS_NONE);
151

152
  gegl_buffer_iterator_add (iter, input, &working_region, 0, babl_format ("Y' float"),
153
                            GEGL_ACCESS_READ, GEGL_ABYSS_NONE);
154

155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172
  while (gegl_buffer_iterator_next (iter))
    {
      gint    i;
      gfloat *data_out = iter->data[0];
      gfloat *data_in  = iter->data[1];

      for (i = 0; i < iter->length; i++)
        {
          /* compute sigmoidal transfer */
          gfloat val = *data_in;
          val = 1.0 / (1.0 + exp (-(SIGMOIDAL_BASE + (o->sharpness * SIGMOIDAL_RANGE)) * (val - 0.5)));
          val = val * o->brightness;
          *data_out = CLAMP (val, 0.0, 1.0);

          data_out +=1;
          data_in  +=1;
        }
    }
173

174
  dest = grey_blur_buffer (dest_tmp, o->glow_radius, result);
175

176
  iter = gegl_buffer_iterator_new (output, result, 0, babl_format ("RGBA float"),
177
                                   GEGL_ACCESS_WRITE, GEGL_ABYSS_NONE);
178

179
  gegl_buffer_iterator_add (iter, input, result, 0, babl_format ("RGBA float"),
180
                            GEGL_ACCESS_READ, GEGL_ABYSS_NONE);
Maxime Nicco's avatar
Maxime Nicco committed
181

182
  gegl_buffer_iterator_add (iter, dest, result, 0, babl_format ("Y' float"),
183
                            GEGL_ACCESS_READ, GEGL_ABYSS_NONE);
184 185

  while (gegl_buffer_iterator_next (iter))
Maxime Nicco's avatar
Maxime Nicco committed
186
    {
187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206
      gint    i;
      gfloat *data_out  = iter->data[0];
      gfloat *data_in   = iter->data[1];
      gfloat *data_blur = iter->data[2];

      for (i = 0; i < iter->length; i++)
        {
          gint c;
          for (c = 0; c < 3; c++)
            {
              gfloat tmp = (1.0 - data_in[c]) * (1.0 - *data_blur);
              data_out[c] = CLAMP (1.0 - tmp, 0.0, 1.0);
            }

          data_out[3] = data_in[3];

          data_out += 4;
          data_in  += 4;
          data_blur+= 1;
        }
Maxime Nicco's avatar
Maxime Nicco committed
207 208 209
    }

  g_object_unref (dest);
210
  g_object_unref (dest_tmp);
211

212
  return TRUE;
Maxime Nicco's avatar
Maxime Nicco committed
213 214 215
}

static void
216
gegl_op_class_init (GeglOpClass *klass)
Maxime Nicco's avatar
Maxime Nicco committed
217 218 219 220 221 222 223
{
  GeglOperationClass       *operation_class;
  GeglOperationFilterClass *filter_class;

  operation_class = GEGL_OPERATION_CLASS (klass);
  filter_class    = GEGL_OPERATION_FILTER_CLASS (klass);

224 225 226
  operation_class->prepare          = prepare;
  operation_class->get_bounding_box = get_bounding_box;
  filter_class->process             = process;
227
  operation_class->threaded         = FALSE;
Maxime Nicco's avatar
Maxime Nicco committed
228 229

  gegl_operation_class_set_keys (operation_class,
230
    "name",        "gegl:softglow",
231
    "title",       _("Softglow"),
232
    "categories",  "artistic",
233
    "license",     "GPL3+",
234 235
    "description", _("Simulate glow by making highlights intense and fuzzy"),
    NULL);
Maxime Nicco's avatar
Maxime Nicco committed
236 237 238
}

#endif