1 /* This file is an image processing operation for GEGL
2  *
3  * GEGL is free software; you can redistribute it and/or
4  * modify it under the terms of the GNU Lesser General Public
5  * License as published by the Free Software Foundation; either
6  * version 3 of the License, or (at your option) any later version.
7  *
8  * GEGL is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11  * Lesser General Public License for more details.
12  *
13  * You should have received a copy of the GNU Lesser General Public
14  * License along with GEGL; if not, see <https://www.gnu.org/licenses/>.
15  *
16  * Copyright 1995 Spencer Kimball and Peter Mattis
17  * Copyright 1996 Torsten Martinsen
18  * Copyright 2000 Tim Copperfield <timecop@japan.co.jp>
19  * Copyright 2012 Maxime Nicco <maxime.nicco@gmail.com>
20  */
21 
22 #include "config.h"
23 #include <glib/gi18n-lib.h>
24 
25 #ifdef GEGL_PROPERTIES
26 
27 property_int  (holdness, _("Dulling"), 2)
28   value_range (1, 8)
29   description (_("A high value lowers the randomness of the noise"))
30 
31 property_double (hue_distance, _("Hue"), 3.0)
32   value_range   (0.0, 180.0)
33 
34 property_double (saturation_distance, _("Saturation"), 0.04)
35   value_range   (0.0, 1.0)
36 
37 property_double (value_distance, _("Value"), 0.04)
38   value_range   (0.0, 1.0)
39 
40 property_seed   (seed, _("Random seed"), rand)
41 
42 #else
43 
44 #define GEGL_OP_POINT_FILTER
45 #define GEGL_OP_NAME     noise_hsv
46 #define GEGL_OP_C_SOURCE noise-hsv.c
47 
48 #include "gegl-op.h"
49 #include <stdio.h>
50 #include <stdlib.h>
51 
52 static gfloat
53 randomize_value (gfloat      now,
54                  gfloat      min,
55                  gfloat      max,
56                  gboolean    wraps_around,
57                  gfloat      rand_max,
58                  gint        holdness,
59                  gint        x,
60                  gint        y,
61                  gint        n,
62                  GeglRandom *rand)
63 {
64   gint    flag, i;
65   gfloat rand_val, new_val, steps;
66 
67   steps = max - min;
68   rand_val = gegl_random_float (rand, x, y, 0, n++);
69 
70   for (i = 1; i < holdness; i++)
71   {
72     gfloat tmp = gegl_random_float (rand, x, y, 0, n++);
73     if (tmp < rand_val)
74       rand_val = tmp;
75   }
76 
77   flag = (gegl_random_float (rand, x, y, 0, n) < 0.5) ? -1 : 1;
78   new_val = now + flag * fmod (rand_max * rand_val, steps);
79 
80   if (new_val < min)
81   {
82     if (wraps_around)
83       new_val += steps;
84     else
85       new_val = min;
86   }
87 
88   if (max < new_val)
89   {
90     if (wraps_around)
91       new_val -= steps;
92     else
93       new_val = max;
94   }
95 
96   return new_val;
97 }
98 
99 static void
100 prepare (GeglOperation *operation)
101 {
102   const Babl *space = gegl_operation_get_source_space (operation, "input");
103   gegl_operation_set_format (operation, "input", babl_format_with_space ("HSVA float", space));
104   gegl_operation_set_format (operation, "output", babl_format_with_space ("HSVA float", space));
105 }
106 
107 static gboolean
108 process (GeglOperation       *operation,
109          void                *in_buf,
110          void                *out_buf,
111          glong                n_pixels,
112          const GeglRectangle *roi,
113          gint                 level)
114 {
115   GeglProperties *o  = GEGL_PROPERTIES (operation);
116   GeglRectangle whole_region;
117   gint i;
118   gint x, y;
119 
120   gfloat   * GEGL_ALIGNED in_pixel;
121   gfloat   * GEGL_ALIGNED out_pixel;
122 
123   gfloat    hue, saturation, value, alpha;
124 
125   in_pixel      = in_buf;
126   out_pixel     = out_buf;
127 
128   x = roi->x;
129   y = roi->y;
130 
131   whole_region = *(gegl_operation_source_get_bounding_box (operation, "input"));
132 
133   for (i = 0; i < n_pixels; i++)
134   {
135     /* n is independent from the roi, but from the whole image */
136     gint n = (3 * o->holdness + 4) * (x + whole_region.width * y);
137 
138     hue        = in_pixel[0];
139     saturation = in_pixel[1];
140     value      = in_pixel[2];
141     alpha      = in_pixel[3];
142 
143     /* there is no need for scattering hue of desaturated pixels here */
144     if ((o->hue_distance > 0) && (saturation > 0))
145       hue = randomize_value (hue, 0.0, 1.0, TRUE, o->hue_distance / 360.0,
146                              o->holdness, x, y, n, o->rand);
147 
148     n += o->holdness + 1;
149     /* desaturated pixels get random hue before increasing saturation */
150     if (o->saturation_distance > 0) {
151       if (saturation == 0)
152         hue = gegl_random_float_range (o->rand, x, y, 0, n, 0.0, 1.0);
153       saturation = randomize_value (saturation, 0.0, 1.0, FALSE,
154                                     o->saturation_distance, o->holdness,
155                                     x, y, n+1, o->rand);
156     }
157 
158     n += o->holdness + 2;
159     if (o->value_distance > 0)
160       value = randomize_value (value, 0.0, 1.0, FALSE, o->value_distance,
161                                o->holdness, x, y, n, o->rand);
162 
163     out_pixel[0] = hue;
164     out_pixel[1] = saturation;
165     out_pixel[2] = value;
166     out_pixel[3] = alpha;
167 
168     in_pixel  += 4;
169     out_pixel += 4;
170 
171     x++;
172     if (x >= roi->x + roi->width)
173       {
174         x = roi->x;
175         y++;
176       }
177   }
178 
179   return TRUE;
180 }
181 
182 #include "opencl/gegl-cl.h"
183 #include "opencl/noise-hsv.cl.h"
184 
185 static GeglClRunData *cl_data = NULL;
186 
187 static gboolean
188 cl_process (GeglOperation       *operation,
189             cl_mem               in,
190             cl_mem               out,
191             size_t               global_worksize,
192             const GeglRectangle *roi,
193             gint                 level)
194 {
195   GeglProperties *o = GEGL_PROPERTIES (operation);
196   GeglRectangle *wr = gegl_operation_source_get_bounding_box (operation,
197                                                               "input");
198   cl_int      cl_err           = 0;
199   cl_mem      cl_random_data   = NULL;
200   cl_int      x_offset         = roi->x;
201   cl_int      y_offset         = roi->y;
202   cl_int      roi_width        = roi->width;
203   cl_int      wr_width         = wr->width;
204   cl_ushort4  rand;
205   cl_int      holdness;
206   cl_float    hue_distance;
207   cl_float    saturation_distance;
208   cl_float    value_distance;
209 
210   gegl_cl_random_get_ushort4 (o->rand, &rand);
211 
212   if (!cl_data)
213     {
214       const char *kernel_name[] = { "cl_noise_hsv", NULL };
215       cl_data = gegl_cl_compile_and_build (noise_hsv_cl_source, kernel_name);
216     }
217 
218   if (!cl_data)
219     return TRUE;
220 
221   cl_random_data = gegl_cl_load_random_data (&cl_err);
222   CL_CHECK;
223 
224   holdness = o->holdness;
225   hue_distance = o->hue_distance / 360.0;
226   saturation_distance = o->saturation_distance;
227   value_distance = o->value_distance;
228 
229   gegl_cl_set_kernel_args (cl_data->kernel[0],
230                            sizeof(cl_mem),     &in,
231                            sizeof(cl_mem),     &out,
232                            sizeof(cl_mem),     &cl_random_data,
233                            sizeof(cl_ushort4), &rand,
234                            sizeof(cl_int),     &x_offset,
235                            sizeof(cl_int),     &y_offset,
236                            sizeof(cl_int),     &roi_width,
237                            sizeof(cl_int),     &wr_width,
238                            sizeof(cl_int),     &holdness,
239                            sizeof(cl_float),   &hue_distance,
240                            sizeof(cl_float),   &saturation_distance,
241                            sizeof(cl_float),   &value_distance,
242                            NULL);
243 
244   CL_CHECK;
245   cl_err = gegl_clEnqueueNDRangeKernel (gegl_cl_get_command_queue (),
246                                         cl_data->kernel[0], 1,
247                                         NULL, &global_worksize, NULL,
248                                         0, NULL, NULL);
249   CL_CHECK;
250 
251   cl_err = gegl_clFinish (gegl_cl_get_command_queue ());
252   CL_CHECK;
253 
254   return  FALSE;
255 
256 error:
257   return TRUE;
258 }
259 
260 static void
261 gegl_op_class_init (GeglOpClass *klass)
262 {
263   GeglOperationClass            *operation_class;
264   GeglOperationPointFilterClass *point_filter_class;
265 
266   operation_class    = GEGL_OPERATION_CLASS (klass);
267   point_filter_class = GEGL_OPERATION_POINT_FILTER_CLASS (klass);
268 
269   operation_class->prepare = prepare;
270   operation_class->opencl_support = TRUE;
271   point_filter_class->process = process;
272   point_filter_class->cl_process = cl_process;
273 
274   gegl_operation_class_set_keys (operation_class,
275     "name",               "gegl:noise-hsv",
276     "title",              _("Add HSV Noise"),
277     "categories",         "noise",
278     "position-dependent", "true",
279     "reference-hash",     "742a94075b6ddfaf86638691bf654b3b",
280     "reference-hashB",    "e750e20f35e03f6c64a38bcc9c11490d",
281     "description", _("Randomize hue, saturation and value independently"),
282       NULL);
283 }
284 
285 #endif
286