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