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 2012,2013 Felix Ulber <felix.ulber@gmx.de> 17 * 2013 Øyvind Kolås <pippin@gimp.org> 18 * 2017 Red Hat, Inc. 19 */ 20 21 #include "config.h" 22 #include <glib/gi18n-lib.h> 23 24 #ifdef GEGL_PROPERTIES 25 26 property_double (black_level, _("Black level"), 0.0) 27 description (_("Adjust the black level")) 28 value_range (-0.1, 0.1) 29 30 property_double (exposure, _("Exposure"), 0.0) 31 description (_("Relative brightness change in stops")) 32 ui_range (-10.0, 10.0) 33 34 #else 35 36 #define GEGL_OP_POINT_FILTER 37 #define GEGL_OP_NAME exposure 38 #define GEGL_OP_C_SOURCE exposure.c 39 40 #include "gegl-op.h" 41 #include "opencl/gegl-cl.h" 42 43 #ifdef _MSC_VER 44 #define exp2f (b) ((gfloat) pow (2.0, b)) 45 #endif 46 47 typedef void (*ProcessFunc) (GeglOperation *operation, 48 void *in_buf, 49 void *out_buf, 50 glong n_pixels, 51 const GeglRectangle *roi, 52 gint level); 53 54 typedef struct 55 { 56 GeglClRunData **cl_data_ptr; 57 ProcessFunc process; 58 const char *kernel_name; 59 const char *kernel_source; 60 } EParamsType; 61 62 static GeglClRunData *cl_data_rgb = NULL; 63 static GeglClRunData *cl_data_rgba = NULL; 64 static GeglClRunData *cl_data_y = NULL; 65 static GeglClRunData *cl_data_ya = NULL; 66 67 static const char* kernel_source_rgb = 68 "__kernel void kernel_exposure_rgb(__global const float *in, \n" 69 " __global float *out, \n" 70 " float black_level, \n" 71 " float gain) \n" 72 "{ \n" 73 " int gid = get_global_id(0); \n" 74 " int offset = 3 * gid; \n" 75 " float3 in_v = (float3) (in[offset], in[offset + 1], in[offset+2]); \n" 76 " float3 out_v; \n" 77 " out_v.xyz = ((in_v.xyz - black_level) * gain); \n" 78 " out[offset] = out_v.x; \n" 79 " out[offset + 1] = out_v.y; \n" 80 " out[offset + 2] = out_v.z; \n" 81 "} \n"; 82 83 static const char* kernel_source_rgba = 84 "__kernel void kernel_exposure_rgba(__global const float4 *in, \n" 85 " __global float4 *out, \n" 86 " float black_level, \n" 87 " float gain) \n" 88 "{ \n" 89 " int gid = get_global_id(0); \n" 90 " float4 in_v = in[gid]; \n" 91 " float4 out_v; \n" 92 " out_v.xyz = ((in_v.xyz - black_level) * gain); \n" 93 " out_v.w = in_v.w; \n" 94 " out[gid] = out_v; \n" 95 "} \n"; 96 97 static const char* kernel_source_y = 98 "__kernel void kernel_exposure_y(__global const float *in, \n" 99 " __global float *out, \n" 100 " float black_level, \n" 101 " float gain) \n" 102 "{ \n" 103 " int gid = get_global_id(0); \n" 104 " float in_v = in[gid]; \n" 105 " float out_v; \n" 106 " out_v = ((in_v - black_level) * gain); \n" 107 " out[gid] = out_v; \n" 108 "} \n"; 109 110 static const char* kernel_source_ya = 111 "__kernel void kernel_exposure_ya(__global const float2 *in, \n" 112 " __global float2 *out, \n" 113 " float black_level, \n" 114 " float gain) \n" 115 "{ \n" 116 " int gid = get_global_id(0); \n" 117 " float2 in_v = in[gid]; \n" 118 " float2 out_v; \n" 119 " out_v.x = ((in_v.x - black_level) * gain); \n" 120 " out_v.y = in_v.y; \n" 121 " out[gid] = out_v; \n" 122 "} \n"; 123 124 static void 125 process_rgb (GeglOperation *op, 126 void *in_buf, 127 void *out_buf, 128 glong n_pixels, 129 const GeglRectangle *roi, 130 gint level) 131 { 132 GeglProperties *o = GEGL_PROPERTIES (op); 133 gfloat *in_pixel; 134 gfloat *out_pixel; 135 gfloat black_level = (gfloat) o->black_level; 136 gfloat diff; 137 gfloat exposure_negated = (gfloat) -o->exposure; 138 gfloat gain; 139 gfloat white; 140 141 glong i; 142 143 in_pixel = in_buf; 144 out_pixel = out_buf; 145 146 white = exp2f (exposure_negated); 147 diff = MAX (white - black_level, 0.000001); 148 gain = 1.0f / diff; 149 150 for (i=0; i<n_pixels; i++) 151 { 152 out_pixel[0] = (in_pixel[0] - black_level) * gain; 153 out_pixel[1] = (in_pixel[1] - black_level) * gain; 154 out_pixel[2] = (in_pixel[2] - black_level) * gain; 155 156 out_pixel += 3; 157 in_pixel += 3; 158 } 159 } 160 161 static void 162 process_rgba (GeglOperation *op, 163 void *in_buf, 164 void *out_buf, 165 glong n_pixels, 166 const GeglRectangle *roi, 167 gint level) 168 { 169 GeglProperties *o = GEGL_PROPERTIES (op); 170 gfloat *in_pixel; 171 gfloat *out_pixel; 172 gfloat black_level = (gfloat) o->black_level; 173 gfloat diff; 174 gfloat exposure_negated = (gfloat) -o->exposure; 175 gfloat gain; 176 gfloat white; 177 178 glong i; 179 180 in_pixel = in_buf; 181 out_pixel = out_buf; 182 183 white = exp2f (exposure_negated); 184 diff = MAX (white - black_level, 0.000001); 185 gain = 1.0f / diff; 186 187 for (i=0; i<n_pixels; i++) 188 { 189 out_pixel[0] = (in_pixel[0] - black_level) * gain; 190 out_pixel[1] = (in_pixel[1] - black_level) * gain; 191 out_pixel[2] = (in_pixel[2] - black_level) * gain; 192 out_pixel[3] = in_pixel[3]; 193 194 out_pixel += 4; 195 in_pixel += 4; 196 } 197 } 198 199 static void 200 process_y (GeglOperation *op, 201 void *in_buf, 202 void *out_buf, 203 glong n_pixels, 204 const GeglRectangle *roi, 205 gint level) 206 { 207 GeglProperties *o = GEGL_PROPERTIES (op); 208 gfloat *in_pixel; 209 gfloat *out_pixel; 210 gfloat black_level = (gfloat) o->black_level; 211 gfloat diff; 212 gfloat exposure_negated = (gfloat) -o->exposure; 213 gfloat gain; 214 gfloat white; 215 216 glong i; 217 218 in_pixel = in_buf; 219 out_pixel = out_buf; 220 221 white = exp2f (exposure_negated); 222 diff = MAX (white - black_level, 0.000001); 223 gain = 1.0f / diff; 224 225 for (i=0; i<n_pixels; i++) 226 { 227 out_pixel[0] = (in_pixel[0] - black_level) * gain; 228 229 out_pixel++; 230 in_pixel++; 231 } 232 } 233 234 static void 235 process_ya (GeglOperation *op, 236 void *in_buf, 237 void *out_buf, 238 glong n_pixels, 239 const GeglRectangle *roi, 240 gint level) 241 { 242 GeglProperties *o = GEGL_PROPERTIES (op); 243 gfloat *in_pixel; 244 gfloat *out_pixel; 245 gfloat black_level = (gfloat) o->black_level; 246 gfloat diff; 247 gfloat exposure_negated = (gfloat) -o->exposure; 248 gfloat gain; 249 gfloat white; 250 251 glong i; 252 253 in_pixel = in_buf; 254 out_pixel = out_buf; 255 256 white = exp2f (exposure_negated); 257 diff = MAX (white - black_level, 0.000001); 258 gain = 1.0f / diff; 259 260 for (i=0; i<n_pixels; i++) 261 { 262 out_pixel[0] = (in_pixel[0] - black_level) * gain; 263 out_pixel[1] = in_pixel[1]; 264 265 out_pixel += 2; 266 in_pixel += 2; 267 } 268 } 269 270 static void 271 prepare (GeglOperation *operation) 272 { 273 GeglProperties *o = GEGL_PROPERTIES (operation); 274 const Babl *space = gegl_operation_get_source_space (operation, "input"); 275 EParamsType *params; 276 const Babl *format; 277 const Babl *input_format; 278 const Babl *input_model; 279 const Babl *y_model; 280 281 if (o->user_data == NULL) 282 o->user_data = g_slice_new0 (EParamsType); 283 284 params = (EParamsType *) o->user_data; 285 286 input_format = gegl_operation_get_source_format (operation, "input"); 287 if (input_format == NULL) 288 { 289 format = babl_format ("RGBA float"); 290 291 params->process = process_rgba; 292 293 params->cl_data_ptr = &cl_data_rgba; 294 params->kernel_name = "kernel_exposure_rgba"; 295 params->kernel_source = kernel_source_rgba; 296 goto out; 297 } 298 299 input_model = babl_format_get_model (input_format); 300 301 if (babl_format_has_alpha (input_format)) 302 { 303 y_model = babl_model_with_space ("YA", space); 304 if (input_model == y_model) 305 { 306 format = babl_format_with_space ("YA float", space); 307 308 params->process = process_ya; 309 310 params->cl_data_ptr = &cl_data_ya; 311 params->kernel_name = "kernel_exposure_ya"; 312 params->kernel_source = kernel_source_ya; 313 } 314 else 315 { 316 format = babl_format_with_space ("RGBA float", space); 317 318 params->process = process_rgba; 319 320 params->cl_data_ptr = &cl_data_rgba; 321 params->kernel_name = "kernel_exposure_rgba"; 322 params->kernel_source = kernel_source_rgba; 323 } 324 } 325 else 326 { 327 y_model = babl_model_with_space ("Y", space); 328 if (input_model == y_model) 329 { 330 format = babl_format_with_space ("Y float", space); 331 332 params->process = process_y; 333 334 params->cl_data_ptr = &cl_data_y; 335 params->kernel_name = "kernel_exposure_y"; 336 params->kernel_source = kernel_source_y; 337 } 338 else 339 { 340 format = babl_format_with_space ("RGB float", space); 341 342 params->process = process_rgb; 343 344 params->cl_data_ptr = &cl_data_rgb; 345 params->kernel_name = "kernel_exposure_rgb"; 346 params->kernel_source = kernel_source_rgb; 347 } 348 } 349 350 out: 351 gegl_operation_set_format (operation, "input", format); 352 gegl_operation_set_format (operation, "output", format); 353 } 354 355 /* GeglOperationPointFilter gives us a linear buffer to operate on 356 * in our requested pixel format 357 */ 358 static gboolean 359 process (GeglOperation *operation, 360 void *in_buf, 361 void *out_buf, 362 glong n_pixels, 363 const GeglRectangle *roi, 364 gint level) 365 { 366 GeglProperties *o = GEGL_PROPERTIES (operation); 367 EParamsType *params = (EParamsType *) o->user_data; 368 369 params->process (operation, in_buf, out_buf, n_pixels, roi, level); 370 return TRUE; 371 } 372 373 /* OpenCL processing function */ 374 static cl_int 375 cl_process (GeglOperation *op, 376 cl_mem in_tex, 377 cl_mem out_tex, 378 size_t global_worksize, 379 const GeglRectangle *roi, 380 gint level) 381 { 382 /* Retrieve a pointer to GeglProperties structure which contains all the 383 * chanted properties 384 */ 385 386 GeglProperties *o = GEGL_PROPERTIES (op); 387 EParamsType *params = (EParamsType *) o->user_data; 388 389 gfloat black_level = (gfloat) o->black_level; 390 gfloat diff; 391 gfloat exposure_negated = (gfloat) -o->exposure; 392 gfloat gain; 393 gfloat white; 394 395 GeglClRunData *cl_data_local; 396 cl_int cl_err = 0; 397 398 if (*params->cl_data_ptr == NULL) 399 { 400 const char *kernel_name[] = {NULL, NULL}; 401 402 kernel_name[0] = params->kernel_name; 403 *params->cl_data_ptr = gegl_cl_compile_and_build (params->kernel_source, kernel_name); 404 } 405 if (*params->cl_data_ptr == NULL) return 1; 406 407 cl_data_local = *params->cl_data_ptr; 408 409 white = exp2f (exposure_negated); 410 diff = MAX (white - black_level, 0.000001); 411 gain = 1.0f / diff; 412 413 cl_err |= gegl_clSetKernelArg(cl_data_local->kernel[0], 0, sizeof(cl_mem), (void*)&in_tex); 414 cl_err |= gegl_clSetKernelArg(cl_data_local->kernel[0], 1, sizeof(cl_mem), (void*)&out_tex); 415 cl_err |= gegl_clSetKernelArg(cl_data_local->kernel[0], 2, sizeof(cl_float), (void*)&black_level); 416 cl_err |= gegl_clSetKernelArg(cl_data_local->kernel[0], 3, sizeof(cl_float), (void*)&gain); 417 if (cl_err != CL_SUCCESS) return cl_err; 418 419 cl_err = gegl_clEnqueueNDRangeKernel(gegl_cl_get_command_queue (), 420 cl_data_local->kernel[0], 1, 421 NULL, &global_worksize, NULL, 422 0, NULL, NULL); 423 if (cl_err != CL_SUCCESS) return cl_err; 424 425 return cl_err; 426 } 427 428 static void 429 finalize (GObject *object) 430 { 431 GeglOperation *op = GEGL_OPERATION (object); 432 GeglProperties *o = GEGL_PROPERTIES (op); 433 434 if (o->user_data) 435 g_slice_free (EParamsType, o->user_data); 436 437 G_OBJECT_CLASS (gegl_op_parent_class)->finalize (object); 438 } 439 440 static void 441 gegl_op_class_init (GeglOpClass *klass) 442 { 443 GObjectClass *object_class; 444 GeglOperationClass *operation_class; 445 GeglOperationPointFilterClass *point_filter_class; 446 gchar *composition = 447 "<?xml version='1.0' encoding='UTF-8'?>" 448 "<gegl>" 449 " <node operation='gegl:crop' width='200' height='200'/>" 450 " <node operation='gegl:over'>" 451 " <node operation='gegl:exposure'>" 452 " <params>" 453 " <param name='exposure'>1.5</param>" 454 " </params>" 455 " </node>" 456 " <node operation='gegl:load' path='standard-input.png'/>" 457 " </node>" 458 " <node operation='gegl:checkerboard'>" 459 " <params>" 460 " <param name='color1'>rgb(0.25,0.25,0.25)</param>" 461 " <param name='color2'>rgb(0.75,0.75,0.75)</param>" 462 " </params>" 463 " </node>" 464 "</gegl>"; 465 466 object_class = G_OBJECT_CLASS (klass); 467 operation_class = GEGL_OPERATION_CLASS (klass); 468 point_filter_class = GEGL_OPERATION_POINT_FILTER_CLASS (klass); 469 470 object_class->finalize = finalize; 471 472 operation_class->opencl_support = TRUE; 473 operation_class->prepare = prepare; 474 475 point_filter_class->process = process; 476 point_filter_class->cl_process = cl_process; 477 478 gegl_operation_class_set_keys (operation_class, 479 "name", "gegl:exposure", 480 "title", _("Exposure"), 481 "categories", "color", 482 "reference-hash", "a4ae5d7f933046aa462e0f7659bd1261", 483 "reference-composition", composition, 484 "description", _("Change exposure of an image in shutter speed stops"), 485 "op-version", "1:0", 486 NULL); 487 } 488 489 #endif 490