contrast-curve.c 7.84 KB
Newer Older
1 2 3 4 5
/* 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
6
 * version 3 of the License, or (at your option) any later version.
7 8 9 10 11 12 13
 *
 * 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 2007 Mark Probst <mark.probst@gmail.com>
 */
18 19 20 21 22

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


23
#ifdef GEGL_PROPERTIES
24

25 26 27 28 29 30
property_int (sampling_points, _("Sample points"), 0)
  description (_("Number of curve sampling points.  0 for exact calculation."))
  value_range (0, 65536)

property_curve (curve, _("Curve"), NULL)
  description (_("The contrast curve."))
31 32 33

#else

34
#define GEGL_OP_POINT_FILTER
35
#define GEGL_OP_NAME     contrast_curve
36
#define GEGL_OP_C_SOURCE contrast-curve.c
37

38
#include "gegl-op.h"
39

40
static void prepare (GeglOperation *operation)
41
{
42 43
  const Babl *space = gegl_operation_get_source_space (operation, "input");
  const Babl *format = babl_format_with_space ("YA float", space);
44 45 46

  gegl_operation_set_format (operation, "input", format);
  gegl_operation_set_format (operation, "output", format);
47 48
}

49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76
#include "opencl/gegl-cl.h"
#include "gegl/gegl-debug.h"

#include "opencl/contrast-curve.cl.h"

/* TODO : Replace gegl_curve_calc_values and gegl_curve_calc_value in cl_process
          with something more suitable for the cl version*/

static void
copy_double_array_to_float_array (gdouble *in,
                                  gfloat  *out,
                                  gint     size)
{
  gint i;
  for (i = 0; i < size; ++i)
    out[i] = (gfloat) in[i];
}

static GeglClRunData * cl_data = NULL;

static gboolean
cl_process (GeglOperation       *self,
            cl_mem               in_tex,
            cl_mem               out_tex,
            size_t               global_worksize,
            const GeglRectangle *roi,
            gint                 level)
{
77
  GeglProperties *o = GEGL_PROPERTIES (self);
78 79
  gint       num_sampling_points;
  gdouble    *xs, *ys;
80 81
  gfloat     *ysf = NULL;
  cl_mem     cl_curve = NULL;
82 83 84 85 86
  cl_ulong   cl_max_constant_size;
  cl_int     cl_err = 0;

  num_sampling_points = o->sampling_points;

87 88 89 90 91 92 93 94 95
  if (!cl_data)
    {
      const char *kernel_name[] = {"cl_contrast_curve",NULL};
      cl_data = gegl_cl_compile_and_build (contrast_curve_cl_source,
                                           kernel_name);
    }
  if (!cl_data)
    return TRUE;

96 97 98 99 100 101
  if (num_sampling_points > 0)
    {
      xs = g_new (gdouble, num_sampling_points);
      ys = g_new (gdouble, num_sampling_points);

      gegl_curve_calc_values (o->curve, 0.0, 1.0, num_sampling_points, xs, ys);
102
      g_free (xs);
103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146

      /*We need to downscale the array to pass it to the GPU*/
      ysf = g_new (gfloat, num_sampling_points);
      copy_double_array_to_float_array (ys, ysf, num_sampling_points);
      g_free (ys);

      cl_err = gegl_clGetDeviceInfo (gegl_cl_get_device (),
                                     CL_DEVICE_MAX_CONSTANT_BUFFER_SIZE,
                                     sizeof (cl_ulong),
                                     &cl_max_constant_size,
                                     NULL);
      CL_CHECK;

      GEGL_NOTE (GEGL_DEBUG_OPENCL, "Max Constant Mem Size: %lu bytes",
                 (unsigned long) cl_max_constant_size);

      if (sizeof (cl_float) * num_sampling_points < cl_max_constant_size)
        {
          cl_curve = gegl_clCreateBuffer (gegl_cl_get_context (),
                                          CL_MEM_USE_HOST_PTR | CL_MEM_READ_ONLY,
                                          num_sampling_points * sizeof (cl_float),
                                          ysf, &cl_err);
          CL_CHECK;
          cl_err = gegl_clSetKernelArg (cl_data->kernel[0], 0, sizeof (cl_mem),
                                        (void*) &in_tex);
          CL_CHECK;
          cl_err = gegl_clSetKernelArg (cl_data->kernel[0], 1, sizeof (cl_mem),
                                        (void*) &out_tex);
          CL_CHECK;
          cl_err = gegl_clSetKernelArg (cl_data->kernel[0], 2, sizeof (cl_mem),
                                        (void*) &cl_curve);
          CL_CHECK;
          cl_err = gegl_clSetKernelArg (cl_data->kernel[0], 3, sizeof (gint),
                                        (void*) &num_sampling_points);
          CL_CHECK;
          cl_err = gegl_clEnqueueNDRangeKernel (gegl_cl_get_command_queue (),
                                                cl_data->kernel[0], 1,
                                                NULL, &global_worksize, NULL,
                                                0, NULL, NULL);
          CL_CHECK;

          cl_err = gegl_clFinish (gegl_cl_get_command_queue ());
          CL_CHECK;

147 148
          cl_err = gegl_clReleaseMemObject (cl_curve);
          CL_CHECK_ONLY (cl_err);
149 150 151 152 153 154 155 156 157 158
        }
      else
        {
          /*If the curve size doesn't fit constant memory is better to use CPU*/
          GEGL_NOTE (GEGL_DEBUG_OPENCL,
                     "Not enough constant memory for the curve");
          g_free (ysf);
          return TRUE;
        }

159
      g_free (ysf);
160 161
      return FALSE;
error:
Debarshi Ray's avatar
Debarshi Ray committed
162
      g_free (ysf);
163 164 165
      if (cl_curve)
        gegl_clReleaseMemObject (cl_curve);

166 167 168 169 170 171 172 173 174 175
      return TRUE;
    }
  else  /*If the curve doesn't have a lookup table is better to use CPU*/
    {
      GEGL_NOTE (GEGL_DEBUG_OPENCL,
                 "Curve not suitable to be computed in the GPU");
      return TRUE;
    }
}

176
static gboolean
177 178 179 180
process (GeglOperation       *op,
         void                *in_buf,
         void                *out_buf,
         glong                samples,
181 182
         const GeglRectangle *roi,
         gint                 level)
183
{
184
  GeglProperties *o = GEGL_PROPERTIES (op);
185 186
  gint        num_sampling_points;
  GeglCurve  *curve;
187
  gint i;
188 189
  gfloat  *in  = in_buf;
  gfloat  *out = out_buf;
190
  gdouble *xs, *ys;
191

192 193
  num_sampling_points = o->sampling_points;
  curve = o->curve;
194 195 196

  if (num_sampling_points > 0)
  {
197 198
    xs = g_new(gdouble, num_sampling_points);
    ys = g_new(gdouble, num_sampling_points);
199

200
    gegl_curve_calc_values(o->curve, 0.0, 1.0, num_sampling_points, xs, ys);
201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229

    g_free(xs);

    for (i=0; i<samples; i++)
    {
      gint x = in[0] * num_sampling_points;
      gfloat y;

      if (x < 0)
       y = ys[0];
      else if (x >= num_sampling_points)
       y = ys[num_sampling_points - 1];
      else
       y = ys[x];

      out[0] = y;
      out[1]=in[1];

      in += 2;
      out+= 2;
    }

    g_free(ys);
  }
  else
    for (i=0; i<samples; i++)
    {
      gfloat u = in[0];

230
      out[0] = gegl_curve_calc_value(curve, u);
231 232 233 234 235 236 237 238 239
      out[1]=in[1];

      in += 2;
      out+= 2;
    }

  return TRUE;
}

240 241

static void
242
gegl_op_class_init (GeglOpClass *klass)
243 244 245 246 247 248 249 250
{
  GeglOperationClass            *operation_class;
  GeglOperationPointFilterClass *point_filter_class;

  operation_class    = GEGL_OPERATION_CLASS (klass);
  point_filter_class = GEGL_OPERATION_POINT_FILTER_CLASS (klass);

  point_filter_class->process = process;
251
  point_filter_class->cl_process = cl_process;
252
  operation_class->prepare = prepare;
253
  operation_class->opencl_support = TRUE;
254
  operation_class->threaded = FALSE; // XXX: recalculate of gegl_curve_calc_value is not thread safe
255

256 257
  gegl_operation_class_set_keys (operation_class,
    "name"       , "gegl:contrast-curve",
258
    "title",       _("Contrast Curve"),
259
    "categories" , "color",
260
    "reference-hash", "43ddd80572ab34095298ac7c36368b0c",
261
    "description",
262
        _("Adjusts the contrast of a grayscale image with a curve specifying contrast for intensity."),
263
        NULL);
264 265
}

266
#endif