exposure.c 15.3 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13
/* 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
14
 * License along with GEGL; if not, see <https://www.gnu.org/licenses/>.
15
 *
16 17
 * Copyright 2012,2013 Felix Ulber <felix.ulber@gmx.de>
 *           2013 Øyvind Kolås <pippin@gimp.org>
18
 *           2017 Red Hat, Inc.
19 20 21 22 23
 */

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

24
#ifdef GEGL_PROPERTIES
25

26
property_double (black_level, _("Black level"), 0.0)
27 28 29
    description (_("Adjust the black level"))
    value_range (-0.1, 0.1)

30 31 32
property_double (exposure, _("Exposure"), 0.0)
    description (_("Relative brightness change in stops"))
    ui_range    (-10.0, 10.0)
33

34 35
#else

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 46
#endif

47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
typedef void (*ProcessFunc) (GeglOperation       *operation,
                             void                *in_buf,
                             void                *out_buf,
                             glong                n_pixels,
                             const GeglRectangle *roi,
                             gint                 level);

typedef struct
{
  GeglClRunData **cl_data_ptr;
  ProcessFunc process;
  const char *kernel_name;
  const char *kernel_source;
} EParamsType;

static GeglClRunData *cl_data_rgb = NULL;
static GeglClRunData *cl_data_rgba = NULL;
64 65
static GeglClRunData *cl_data_y = NULL;
static GeglClRunData *cl_data_ya = NULL;
66 67

static const char* kernel_source_rgb =
68 69 70 71
"__kernel void kernel_exposure_rgb(__global const float *in,           \n"
"                                  __global       float *out,          \n"
"                                  float                 black_level,  \n"
"                                  float                 gain)         \n"
72 73
"{                                                                     \n"
"  int gid = get_global_id(0);                                         \n"
74 75
"  int offset  = 3 * gid;                                              \n"
"  float3 in_v = (float3) (in[offset], in[offset + 1], in[offset+2]);  \n"
76 77
"  float3 out_v;                                                       \n"
"  out_v.xyz =  ((in_v.xyz - black_level) * gain);                     \n"
78 79 80
"  out[offset]     = out_v.x;                                          \n"
"  out[offset + 1] = out_v.y;                                          \n"
"  out[offset + 2] = out_v.z;                                          \n"
81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96
"}                                                                     \n";

static const char* kernel_source_rgba =
"__kernel void kernel_exposure_rgba(__global const float4 *in,          \n"
"                                   __global       float4 *out,         \n"
"                                   float                  black_level, \n"
"                                   float                  gain)        \n"
"{                                                                      \n"
"  int gid = get_global_id(0);                                          \n"
"  float4 in_v  = in[gid];                                              \n"
"  float4 out_v;                                                        \n"
"  out_v.xyz =  ((in_v.xyz - black_level) * gain);                      \n"
"  out_v.w   =  in_v.w;                                                 \n"
"  out[gid]  =  out_v;                                                  \n"
"}                                                                      \n";

97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123
static const char* kernel_source_y =
"__kernel void kernel_exposure_y(__global const float *in,             \n"
"                                __global       float *out,            \n"
"                                float                 black_level,    \n"
"                                float                 gain)           \n"
"{                                                                     \n"
"  int gid = get_global_id(0);                                         \n"
"  float in_v  = in[gid];                                              \n"
"  float out_v;                                                        \n"
"  out_v     =  ((in_v - black_level) * gain);                         \n"
"  out[gid]  =  out_v;                                                 \n"
"}                                                                     \n";

static const char* kernel_source_ya =
"__kernel void kernel_exposure_ya(__global const float2 *in,             \n"
"                                 __global       float2 *out,            \n"
"                                 float                  black_level,    \n"
"                                 float                  gain)           \n"
"{                                                                       \n"
"  int gid = get_global_id(0);                                           \n"
"  float2 in_v  = in[gid];                                               \n"
"  float2 out_v;                                                         \n"
"  out_v.x   =  ((in_v.x - black_level) * gain);                         \n"
"  out_v.y   =  in_v.y;                                                  \n"
"  out[gid]  =  out_v;                                                   \n"
"}                                                                       \n";

124
static void
125 126 127 128 129 130
process_rgb (GeglOperation       *op,
             void                *in_buf,
             void                *out_buf,
             glong                n_pixels,
             const GeglRectangle *roi,
             gint                 level)
131
{
132 133 134 135 136 137 138 139 140 141 142 143 144 145 146
  GeglProperties *o = GEGL_PROPERTIES (op);
  gfloat     *in_pixel;
  gfloat     *out_pixel;
  gfloat      black_level = (gfloat) o->black_level;
  gfloat      diff;
  gfloat      exposure_negated = (gfloat) -o->exposure;
  gfloat      gain;
  gfloat      white;

  glong       i;

  in_pixel = in_buf;
  out_pixel = out_buf;

  white = exp2f (exposure_negated);
147
  diff = MAX (white - black_level, 0.000001);
148 149 150 151 152 153 154 155 156 157 158
  gain = 1.0f / diff;

  for (i=0; i<n_pixels; i++)
    {
      out_pixel[0] = (in_pixel[0] - black_level) * gain;
      out_pixel[1] = (in_pixel[1] - black_level) * gain;
      out_pixel[2] = (in_pixel[2] - black_level) * gain;

      out_pixel += 3;
      in_pixel  += 3;
    }
159 160
}

161 162 163 164 165 166 167
static void
process_rgba (GeglOperation       *op,
              void                *in_buf,
              void                *out_buf,
              glong                n_pixels,
              const GeglRectangle *roi,
              gint                 level)
168
{
169
  GeglProperties *o = GEGL_PROPERTIES (op);
170 171
  gfloat     *in_pixel;
  gfloat     *out_pixel;
172
  gfloat      black_level = (gfloat) o->black_level;
173 174 175 176
  gfloat      diff;
  gfloat      exposure_negated = (gfloat) -o->exposure;
  gfloat      gain;
  gfloat      white;
177 178 179 180 181 182
  
  glong       i;

  in_pixel = in_buf;
  out_pixel = out_buf;
  
183
  white = exp2f (exposure_negated);
184
  diff = MAX (white - black_level, 0.000001);
185 186
  gain = 1.0f / diff;

187 188 189 190 191 192 193 194 195 196
  for (i=0; i<n_pixels; i++)
    {
      out_pixel[0] = (in_pixel[0] - black_level) * gain;
      out_pixel[1] = (in_pixel[1] - black_level) * gain;
      out_pixel[2] = (in_pixel[2] - black_level) * gain;
      out_pixel[3] = in_pixel[3];
      
      out_pixel += 4;
      in_pixel  += 4;
    }
197 198
}

199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221
static void
process_y (GeglOperation       *op,
           void                *in_buf,
           void                *out_buf,
           glong                n_pixels,
           const GeglRectangle *roi,
           gint                 level)
{
  GeglProperties *o = GEGL_PROPERTIES (op);
  gfloat     *in_pixel;
  gfloat     *out_pixel;
  gfloat      black_level = (gfloat) o->black_level;
  gfloat      diff;
  gfloat      exposure_negated = (gfloat) -o->exposure;
  gfloat      gain;
  gfloat      white;

  glong       i;

  in_pixel = in_buf;
  out_pixel = out_buf;

  white = exp2f (exposure_negated);
222
  diff = MAX (white - black_level, 0.000001);
223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256
  gain = 1.0f / diff;

  for (i=0; i<n_pixels; i++)
    {
      out_pixel[0] = (in_pixel[0] - black_level) * gain;

      out_pixel++;
      in_pixel++;
    }
}

static void
process_ya (GeglOperation       *op,
            void                *in_buf,
            void                *out_buf,
            glong                n_pixels,
            const GeglRectangle *roi,
            gint                 level)
{
  GeglProperties *o = GEGL_PROPERTIES (op);
  gfloat     *in_pixel;
  gfloat     *out_pixel;
  gfloat      black_level = (gfloat) o->black_level;
  gfloat      diff;
  gfloat      exposure_negated = (gfloat) -o->exposure;
  gfloat      gain;
  gfloat      white;

  glong       i;

  in_pixel = in_buf;
  out_pixel = out_buf;

  white = exp2f (exposure_negated);
257
  diff = MAX (white - black_level, 0.000001);
258 259 260 261 262 263 264 265 266 267 268 269
  gain = 1.0f / diff;

  for (i=0; i<n_pixels; i++)
    {
      out_pixel[0] = (in_pixel[0] - black_level) * gain;
      out_pixel[1] = in_pixel[1];

      out_pixel += 2;
      in_pixel  += 2;
    }
}

270 271 272 273
static void
prepare (GeglOperation *operation)
{
  GeglProperties *o = GEGL_PROPERTIES (operation);
274
  const Babl *space = gegl_operation_get_source_space (operation, "input");
275 276 277
  EParamsType *params;
  const Babl *format;
  const Babl *input_format;
278 279
  const Babl *input_model;
  const Babl *y_model;
280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298

  if (o->user_data == NULL)
    o->user_data = g_slice_new0 (EParamsType);

  params = (EParamsType *) o->user_data;

  input_format = gegl_operation_get_source_format (operation, "input");
  if (input_format == NULL)
    {
      format = babl_format ("RGBA float");

      params->process = process_rgba;

      params->cl_data_ptr = &cl_data_rgba;
      params->kernel_name = "kernel_exposure_rgba";
      params->kernel_source = kernel_source_rgba;
      goto out;
    }

299 300
  input_model = babl_format_get_model (input_format);

301 302
  if (babl_format_has_alpha (input_format))
    {
303
      y_model = babl_model_with_space ("YA", space);
304 305
      if (input_model == y_model)
        {
306
          format = babl_format_with_space ("YA float", space);
307 308 309 310 311 312 313 314 315

          params->process = process_ya;

          params->cl_data_ptr = &cl_data_ya;
          params->kernel_name = "kernel_exposure_ya";
          params->kernel_source = kernel_source_ya;
        }
      else
        {
316
          format = babl_format_with_space ("RGBA float", space);
317 318 319 320 321 322 323

          params->process = process_rgba;

          params->cl_data_ptr = &cl_data_rgba;
          params->kernel_name = "kernel_exposure_rgba";
          params->kernel_source = kernel_source_rgba;
        }
324 325 326
    }
  else
    {
327
      y_model = babl_model_with_space ("Y", space);
328 329
      if (input_model == y_model)
        {
330
          format = babl_format_with_space ("Y float", space);
331 332 333 334 335 336 337 338 339

          params->process = process_y;

          params->cl_data_ptr = &cl_data_y;
          params->kernel_name = "kernel_exposure_y";
          params->kernel_source = kernel_source_y;
        }
      else
        {
340
          format = babl_format_with_space ("RGB float", space);
341 342 343 344 345 346 347

          params->process = process_rgb;

          params->cl_data_ptr = &cl_data_rgb;
          params->kernel_name = "kernel_exposure_rgb";
          params->kernel_source = kernel_source_rgb;
        }
348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367
    }

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

/* GeglOperationPointFilter gives us a linear buffer to operate on
 * in our requested pixel format
 */
static gboolean
process (GeglOperation       *operation,
         void                *in_buf,
         void                *out_buf,
         glong                n_pixels,
         const GeglRectangle *roi,
         gint                 level)
{
  GeglProperties *o = GEGL_PROPERTIES (operation);
  EParamsType *params = (EParamsType *) o->user_data;
368

369 370 371
  params->process (operation, in_buf, out_buf, n_pixels, roi, level);
  return TRUE;
}
372 373 374 375 376 377 378 379 380 381

/* OpenCL processing function */
static cl_int
cl_process (GeglOperation       *op,
            cl_mem               in_tex,
            cl_mem               out_tex,
            size_t               global_worksize,
            const GeglRectangle *roi,
            gint                 level)
{
382
  /* Retrieve a pointer to GeglProperties structure which contains all the
383 384 385
   * chanted properties
   */

386
  GeglProperties *o = GEGL_PROPERTIES (op);
387
  EParamsType *params = (EParamsType *) o->user_data;
388

389
  gfloat      black_level = (gfloat) o->black_level;
390 391 392 393
  gfloat      diff;
  gfloat      exposure_negated = (gfloat) -o->exposure;
  gfloat      gain;
  gfloat      white;
394
  
395
  GeglClRunData *cl_data_local;
396 397
  cl_int cl_err = 0;

398
  if (*params->cl_data_ptr == NULL)
399
    {
400 401 402 403
      const char *kernel_name[] = {NULL, NULL};

      kernel_name[0] = params->kernel_name;
      *params->cl_data_ptr = gegl_cl_compile_and_build (params->kernel_source, kernel_name);
404
    }
405 406 407
  if (*params->cl_data_ptr == NULL) return 1;

  cl_data_local = *params->cl_data_ptr;
408

409
  white = exp2f (exposure_negated);
410
  diff = MAX (white - black_level, 0.000001);
411 412
  gain = 1.0f / diff;

413 414 415 416
  cl_err |= gegl_clSetKernelArg(cl_data_local->kernel[0], 0, sizeof(cl_mem),   (void*)&in_tex);
  cl_err |= gegl_clSetKernelArg(cl_data_local->kernel[0], 1, sizeof(cl_mem),   (void*)&out_tex);
  cl_err |= gegl_clSetKernelArg(cl_data_local->kernel[0], 2, sizeof(cl_float), (void*)&black_level);
  cl_err |= gegl_clSetKernelArg(cl_data_local->kernel[0], 3, sizeof(cl_float), (void*)&gain);
417 418 419
  if (cl_err != CL_SUCCESS) return cl_err;

  cl_err = gegl_clEnqueueNDRangeKernel(gegl_cl_get_command_queue (),
420
                                        cl_data_local->kernel[0], 1,
421 422 423 424 425 426 427
                                        NULL, &global_worksize, NULL,
                                        0, NULL, NULL);
  if (cl_err != CL_SUCCESS) return cl_err;

  return cl_err;
}

428 429 430 431 432 433 434 435 436 437 438 439
static void
finalize (GObject *object)
{
  GeglOperation *op = GEGL_OPERATION (object);
  GeglProperties *o = GEGL_PROPERTIES (op);

  if (o->user_data)
    g_slice_free (EParamsType, o->user_data);

  G_OBJECT_CLASS (gegl_op_parent_class)->finalize (object);
}

440
static void
441
gegl_op_class_init (GeglOpClass *klass)
442
{
443
  GObjectClass                  *object_class;
444 445 446
  GeglOperationClass            *operation_class;
  GeglOperationPointFilterClass *point_filter_class;

447
  object_class       = G_OBJECT_CLASS (klass);
448 449 450
  operation_class    = GEGL_OPERATION_CLASS (klass);
  point_filter_class = GEGL_OPERATION_POINT_FILTER_CLASS (klass);

451 452
  object_class->finalize = finalize;

453
  operation_class->opencl_support = TRUE;
454 455 456 457
  operation_class->prepare        = prepare;

  point_filter_class->process    = process;
  point_filter_class->cl_process = cl_process;
458 459

  gegl_operation_class_set_keys (operation_class,
460
    "name",        "gegl:exposure",
461
    "title",       _("Exposure"),
462
    "categories",  "color",
463
    "reference-hash", "967bd5777363d1fec59f04889f358a99",
464
    "reference-chain", "load path=images/standard-input.png exposure exposure=1.5",
465
    "description", _("Change exposure of an image in shutter speed stops"),
466
    "op-version",  "1:0",
467 468 469 470
    NULL);
}

#endif