graduatednd.c 41.6 KB
Newer Older
1 2
/*
    This file is part of darktable,
3
    copyright (c) 2010-2012 Henrik Andersson.
4 5 6 7 8 9 10 11 12 13 14 15 16 17 18

    darktable is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    darktable 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 General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with darktable.  If not, see <http://www.gnu.org/licenses/>.
*/
#ifdef HAVE_CONFIG_H
19
#include "config.h"
20 21
#endif
#include <assert.h>
22 23
#include <math.h>
#include <stdlib.h>
24
#include <string.h>
25

26
#include "bauhaus/bauhaus.h"
27
#include "common/colorspaces.h"
28
#include "common/debug.h"
29
#include "common/opencl.h"
30 31 32 33 34
#include "control/control.h"
#include "develop/develop.h"
#include "develop/imageop.h"
#include "develop/imageop_math.h"
#include "develop/tiling.h"
Henrik Andersson's avatar
Henrik Andersson committed
35
#include "dtgtk/gradientslider.h"
36
#include "gui/accelerators.h"
37
#include "gui/gtk.h"
38
#include "gui/presets.h"
39
#include "iop/iop_api.h"
40

41
#if defined(__SSE__)
42
#include <xmmintrin.h>
43
#endif
44

45
#define CLIP(x) ((x < 0.0f) ? 0.0f : (x > 1.0f) ? 1.0f : x)
46

47
DT_MODULE_INTROSPECTION(1, dt_iop_graduatednd_params_t)
48 49 50

typedef struct dt_iop_graduatednd_params_t
{
51 52 53 54 55 56
  float density;     // The density of filter 0-8 EV
  float compression; // Default 0% = soft and 100% = hard
  float rotation;    // 2*PI -180 - +180
  float offset;      // Default 50%, centered, can be offsetted...
  float hue;         // the hue
  float saturation;  // the saturation
57
} dt_iop_graduatednd_params_t;
58

59 60 61 62
typedef struct dt_iop_graduatednd_global_data_t
{
  int kernel_graduatedndp;
  int kernel_graduatedndm;
63
} dt_iop_graduatednd_global_data_t;
64 65


66
void init_presets(dt_iop_module_so_t *self)
67
{
68
  DT_DEBUG_SQLITE3_EXEC(dt_database_get(darktable.db), "BEGIN", NULL, NULL, NULL);
69

70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108
  dt_gui_presets_add_generic(_("neutral gray ND2 (soft)"), self->op, self->version(),
                             &(dt_iop_graduatednd_params_t){ 1, 0, 0, 50, 0, 0 },
                             sizeof(dt_iop_graduatednd_params_t), 1);
  dt_gui_presets_add_generic(_("neutral gray ND4 (soft)"), self->op, self->version(),
                             &(dt_iop_graduatednd_params_t){ 2, 0, 0, 50, 0, 0 },
                             sizeof(dt_iop_graduatednd_params_t), 1);
  dt_gui_presets_add_generic(_("neutral gray ND8 (soft)"), self->op, self->version(),
                             &(dt_iop_graduatednd_params_t){ 3, 0, 0, 50, 0, 0 },
                             sizeof(dt_iop_graduatednd_params_t), 1);
  dt_gui_presets_add_generic(_("neutral gray ND2 (hard)"), self->op, self->version(),
                             &(dt_iop_graduatednd_params_t){ 1, 75, 0, 50, 0, 0 },
                             sizeof(dt_iop_graduatednd_params_t), 1);
  dt_gui_presets_add_generic(_("neutral gray ND4 (hard)"), self->op, self->version(),
                             &(dt_iop_graduatednd_params_t){ 2, 75, 0, 50, 0, 0 },
                             sizeof(dt_iop_graduatednd_params_t), 1);
  dt_gui_presets_add_generic(_("neutral gray ND8 (hard)"), self->op, self->version(),
                             &(dt_iop_graduatednd_params_t){ 3, 75, 0, 50, 0, 0 },
                             sizeof(dt_iop_graduatednd_params_t), 1);
  dt_gui_presets_add_generic(_("orange ND2 (soft)"), self->op, self->version(),
                             &(dt_iop_graduatednd_params_t){ 1, 0, 0, 50, 0.102439, 0.8 },
                             sizeof(dt_iop_graduatednd_params_t), 1);
  dt_gui_presets_add_generic(_("yellow ND2 (soft)"), self->op, self->version(),
                             &(dt_iop_graduatednd_params_t){ 1, 0, 0, 50, 0.151220, 0.5 },
                             sizeof(dt_iop_graduatednd_params_t), 1);
  dt_gui_presets_add_generic(_("purple ND2 (soft)"), self->op, self->version(),
                             &(dt_iop_graduatednd_params_t){ 1, 0, 0, 50, 0.824390, 0.5 },
                             sizeof(dt_iop_graduatednd_params_t), 1);
  dt_gui_presets_add_generic(_("green ND2 (soft)"), self->op, self->version(),
                             &(dt_iop_graduatednd_params_t){ 1, 0, 0, 50, 0.302439, 0.5 },
                             sizeof(dt_iop_graduatednd_params_t), 1);
  dt_gui_presets_add_generic(_("red ND2 (soft)"), self->op, self->version(),
                             &(dt_iop_graduatednd_params_t){ 1, 0, 0, 50, 0, 0.5 },
                             sizeof(dt_iop_graduatednd_params_t), 1);
  dt_gui_presets_add_generic(_("blue ND2 (soft)"), self->op, self->version(),
                             &(dt_iop_graduatednd_params_t){ 1, 0, 0, 50, 0.663415, 0.5 },
                             sizeof(dt_iop_graduatednd_params_t), 1);
  dt_gui_presets_add_generic(_("brown ND4 (soft)"), self->op, self->version(),
                             &(dt_iop_graduatednd_params_t){ 2, 0, 0, 50, 0.082927, 0.25 },
                             sizeof(dt_iop_graduatednd_params_t), 1);
109

110
  DT_DEBUG_SQLITE3_EXEC(dt_database_get(darktable.db), "COMMIT", NULL, NULL, NULL);
111 112
}

113 114
typedef struct dt_iop_graduatednd_gui_data_t
{
115
  GtkBox *vbox;
116 117 118
  GtkWidget *label1, *label2, *label3, *label5, *label6; // density, compression, rotation, hue, saturation
  GtkWidget *scale1, *scale2, *scale3;                   // density, compression, rotation
  GtkWidget *gslider1, *gslider2;                        // hue, saturation
Tobias Ellinghaus's avatar
Tobias Ellinghaus committed
119

alicvb's avatar
alicvb committed
120 121
  int selected;
  int dragging;
Tobias Ellinghaus's avatar
Tobias Ellinghaus committed
122

123
  gboolean define;
124 125
  float xa, ya, xb, yb, oldx, oldy;
} dt_iop_graduatednd_gui_data_t;
126 127 128

typedef struct dt_iop_graduatednd_data_t
{
129 130 131 132
  float density;     // The density of filter 0-8 EV
  float compression; // Default 0% = soft and 100% = hard
  float rotation;    // 2*PI -180 - +180
  float offset;      // Default 50%, centered, can be offsetted...
133 134
  float color[4];    // RGB color of gradient
  float color1[4];   // inverted color (1 - c)
135
} dt_iop_graduatednd_data_t;
136 137 138

const char *name()
{
139
  return _("graduated density");
140 141
}

142 143
int flags()
{
144 145
  return IOP_FLAGS_INCLUDE_IN_STYLES | IOP_FLAGS_SUPPORTS_BLENDING | IOP_FLAGS_ALLOW_TILING
         | IOP_FLAGS_TILING_FULL_ROI;
146
}
147

148
int groups()
149 150 151 152
{
  return IOP_GROUP_EFFECT;
}

153
void init_key_accels(dt_iop_module_so_t *self)
154
{
155 156
  dt_accel_register_slider_iop(self, FALSE, NC_("accel", "density"));
  dt_accel_register_slider_iop(self, FALSE, NC_("accel", "compression"));
157
}
158 159 160

void connect_key_accels(dt_iop_module_t *self)
{
161
  dt_iop_graduatednd_gui_data_t *g = (dt_iop_graduatednd_gui_data_t *)self->gui_data;
162 163 164 165 166

  dt_accel_connect_slider_iop(self, "density", GTK_WIDGET(g->scale1));
  dt_accel_connect_slider_iop(self, "compression", GTK_WIDGET(g->scale2));
}

167
static inline float f(const float t, const float c, const float x)
johannes hanika's avatar
johannes hanika committed
168
{
169
  return (t / (1.0f + powf(c, -x * 6.0f)) + (1.0f - t) * (x * .5f + .5f));
johannes hanika's avatar
johannes hanika committed
170
}
171 172 173 174 175 176 177

typedef struct dt_iop_vector_2d_t
{
  double x;
  double y;
} dt_iop_vector_2d_t;

178 179
// determine the distance between the segment [(xa,ya)(xb,yb)] and the point (xc,yc)
static float dist_seg(float xa, float ya, float xb, float yb, float xc, float yc)
180
{
181
  if(xa == xb && ya == yb) return (xc - xa) * (xc - xa) + (yc - ya) * (yc - ya);
Tobias Ellinghaus's avatar
Tobias Ellinghaus committed
182

183 184
  float sx = xb - xa;
  float sy = yb - ya;
Tobias Ellinghaus's avatar
Tobias Ellinghaus committed
185

186 187
  float ux = xc - xa;
  float uy = yc - ya;
Tobias Ellinghaus's avatar
Tobias Ellinghaus committed
188

189 190
  float dp = sx * ux + sy * uy;
  if(dp < 0) return (xc - xa) * (xc - xa) + (yc - ya) * (yc - ya);
Tobias Ellinghaus's avatar
Tobias Ellinghaus committed
191

192 193
  float sn2 = sx * sx + sy * sy;
  if(dp > sn2) return (xc - xb) * (xc - xb) + (yc - yb) * (yc - yb);
194

195 196 197
  float ah2 = dp * dp / sn2;
  float un2 = ux * ux + uy * uy;
  return un2 - ah2;
198 199
}

200 201
static int set_grad_from_points(struct dt_iop_module_t *self, float xa, float ya, float xb, float yb,
                                float *rotation, float *offset)
202
{
203 204 205 206 207 208
  // we want absolute positions
  float pts[4]
      = { xa * self->dev->preview_pipe->backbuf_width, ya * self->dev->preview_pipe->backbuf_height,
          xb * self->dev->preview_pipe->backbuf_width, yb * self->dev->preview_pipe->backbuf_height };
  dt_dev_distort_backtransform_plus(self->dev, self->dev->preview_pipe, self->priority + 1, 9999999, pts, 2);
  dt_dev_pixelpipe_iop_t *piece = dt_dev_distort_get_iop_pipe(self->dev, self->dev->preview_pipe, self);
209 210 211 212
  pts[0] /= (float)piece->buf_out.width;
  pts[2] /= (float)piece->buf_out.width;
  pts[1] /= (float)piece->buf_out.height;
  pts[3] /= (float)piece->buf_out.height;
Tobias Ellinghaus's avatar
Tobias Ellinghaus committed
213

214 215 216 217 218
  // we first need to find the rotation angle
  // weird dichotomic solution : we may use something more cool ...
  float v1 = -M_PI;
  float v2 = M_PI;
  float sinv, cosv, r1, r2, v, r;
Tobias Ellinghaus's avatar
Tobias Ellinghaus committed
219

220
  sinv = sinf(v1), cosv = cosf(v1);
221
  r1 = pts[1] * cosv - pts[0] * sinv + pts[2] * sinv - pts[3] * cosv;
Tobias Ellinghaus's avatar
Tobias Ellinghaus committed
222

223 224
  // we search v2 so r2 as not the same sign as r1
  float pas = M_PI / 16.0;
225 226 227 228
  do
  {
    v2 += pas;
    sinv = sinf(v2), cosv = cosf(v2);
229 230 231
    r2 = pts[1] * cosv - pts[0] * sinv + pts[2] * sinv - pts[3] * cosv;
    if(r1 * r2 < 0) break;
  } while(v2 <= M_PI);
Tobias Ellinghaus's avatar
Tobias Ellinghaus committed
232

233
  if(v2 == M_PI) return 9;
234 235 236 237

  int iter = 0;
  do
  {
238
    v = (v1 + v2) / 2.0;
239
    sinv = sinf(v), cosv = cosf(v);
240
    r = pts[1] * cosv - pts[0] * sinv + pts[2] * sinv - pts[3] * cosv;
Tobias Ellinghaus's avatar
Tobias Ellinghaus committed
241

242
    if(r < 0.01 && r > -0.01) break;
Tobias Ellinghaus's avatar
Tobias Ellinghaus committed
243

244 245
    if(r * r2 < 0)
      v1 = v;
246 247 248 249 250
    else
    {
      r2 = r;
      v2 = v;
    }
Tobias Ellinghaus's avatar
Tobias Ellinghaus committed
251

252 253
  } while(iter++ < 1000);
  if(iter >= 1000) return 8;
Tobias Ellinghaus's avatar
Tobias Ellinghaus committed
254

255 256 257
  // be careful to the gnd direction
  if(pts[2] - pts[0] > 0 && v > M_PI * 0.5) v = v - M_PI;
  if(pts[2] - pts[0] > 0 && v < -M_PI * 0.5) v = M_PI + v;
Tobias Ellinghaus's avatar
Tobias Ellinghaus committed
258

259 260
  if(pts[2] - pts[0] < 0 && v < M_PI * 0.5 && v >= 0) v = v - M_PI;
  if(pts[2] - pts[0] < 0 && v > -M_PI * 0.5 && v < 0) v = v + M_PI;
Tobias Ellinghaus's avatar
Tobias Ellinghaus committed
261

262
  *rotation = -v * 180.0 / M_PI;
Tobias Ellinghaus's avatar
Tobias Ellinghaus committed
263

264 265 266 267 268
  // and now we go for the offset (more easy)
  sinv = sinf(v);
  cosv = cosf(v);
  float ofs = -2.0 * sinv * pts[0] + sinv - cosv + 1.0 + 2.0 * cosv * pts[1];
  *offset = ofs * 50.0;
Tobias Ellinghaus's avatar
Tobias Ellinghaus committed
269

270 271 272
  return 1;
}

273 274
static int set_points_from_grad(struct dt_iop_module_t *self, float *xa, float *ya, float *xb, float *yb,
                                float rotation, float offset)
275
{
276 277 278
  // we get the extremities of the line
  const float v = (-rotation / 180) * M_PI;
  const float sinv = sin(v);
279
  float pts[4];
280 281 282

  dt_dev_pixelpipe_iop_t *piece = dt_dev_distort_get_iop_pipe(self->dev, self->dev->preview_pipe, self);
  if(!piece) return 0;
283
  float wp = piece->buf_out.width, hp = piece->buf_out.height;
284 285 286

  // if sinv=0 then this is just the offset
  if(sinv == 0)
alicvb's avatar
alicvb committed
287
  {
288
    if(v == 0)
alicvb's avatar
alicvb committed
289
    {
290 291 292
      pts[0] = wp * 0.1;
      pts[2] = wp * 0.9;
      pts[1] = pts[3] = hp * offset / 100.0;
alicvb's avatar
alicvb committed
293 294 295
    }
    else
    {
296 297 298
      pts[2] = wp * 0.1;
      pts[0] = wp * 0.9;
      pts[1] = pts[3] = hp * (1.0 - offset / 100.0);
alicvb's avatar
alicvb committed
299
    }
300
  }
301
  else
alicvb's avatar
alicvb committed
302
  {
303 304 305 306
    // otherwise we determine the extremities
    const float cosv = cos(v);
    float xx1 = (sinv - cosv + 1.0 - offset / 50.0) * wp * 0.5 / sinv;
    float xx2 = (sinv + cosv + 1.0 - offset / 50.0) * wp * 0.5 / sinv;
307
    float yy1 = 0;
308
    float yy2 = hp;
309 310 311 312
    float a = hp / (xx2 - xx1);
    float b = -xx1 * a;
    // now ensure that the line isn't outside image borders
    if(xx2 > wp)
alicvb's avatar
alicvb committed
313
    {
314 315
      yy2 = a * wp + b;
      xx2 = wp;
alicvb's avatar
alicvb committed
316
    }
317
    if(xx2 < 0)
alicvb's avatar
alicvb committed
318
    {
319
      yy2 = b;
320
      xx2 = 0;
alicvb's avatar
alicvb committed
321
    }
322
    if(xx1 > wp)
alicvb's avatar
alicvb committed
323
    {
324 325
      yy1 = a * wp + b;
      xx1 = wp;
326
    }
327
    if(xx1 < 0)
328 329
    {
      yy1 = b;
330
      xx1 = 0;
331
    }
Tobias Ellinghaus's avatar
Tobias Ellinghaus committed
332

333 334 335 336 337
    // we want extremities not to be on image border
    xx2 -= (xx2 - xx1) * 0.1;
    xx1 += (xx2 - xx1) * 0.1;
    yy2 -= (yy2 - yy1) * 0.1;
    yy1 += (yy2 - yy1) * 0.1;
Tobias Ellinghaus's avatar
Tobias Ellinghaus committed
338

339
    // now we have to decide which point is where, depending of the angle
340 341 342 343
    /*xx1 /= wd;
    xx2 /= wd;
    yy1 /= ht;
    yy2 /= ht;*/
344
    if(v < M_PI * 0.5 && v > -M_PI * 0.5)
345
    {
346 347
      // we want xa < xb
      if(xx1 < xx2)
348 349 350 351 352 353 354 355 356 357 358 359 360
      {
        pts[0] = xx1;
        pts[1] = yy1;
        pts[2] = xx2;
        pts[3] = yy2;
      }
      else
      {
        pts[2] = xx1;
        pts[3] = yy1;
        pts[0] = xx2;
        pts[1] = yy2;
      }
alicvb's avatar
alicvb committed
361 362 363
    }
    else
    {
364 365
      // we want xb < xa
      if(xx2 < xx1)
366 367 368 369 370 371 372 373 374 375 376 377 378
      {
        pts[0] = xx1;
        pts[1] = yy1;
        pts[2] = xx2;
        pts[3] = yy2;
      }
      else
      {
        pts[2] = xx1;
        pts[3] = yy1;
        pts[0] = xx2;
        pts[1] = yy2;
      }
alicvb's avatar
alicvb committed
379
    }
Tobias Ellinghaus's avatar
Tobias Ellinghaus committed
380
  }
381 382 383 384 385 386 387 388
  // now we want that points to take care of distort modules

  if(!dt_dev_distort_transform_plus(self->dev, self->dev->preview_pipe, self->priority + 1, 999999, pts, 2))
    return 0;
  *xa = pts[0] / self->dev->preview_pipe->backbuf_width;
  *ya = pts[1] / self->dev->preview_pipe->backbuf_height;
  *xb = pts[2] / self->dev->preview_pipe->backbuf_width;
  *yb = pts[3] / self->dev->preview_pipe->backbuf_height;
alicvb's avatar
alicvb committed
389 390 391
  return 1;
}

392 393
void gui_post_expose(struct dt_iop_module_t *self, cairo_t *cr, int32_t width, int32_t height,
                     int32_t pointerx, int32_t pointery)
alicvb's avatar
alicvb committed
394 395 396
{
  dt_develop_t *dev = self->dev;
  dt_iop_graduatednd_gui_data_t *g = (dt_iop_graduatednd_gui_data_t *)self->gui_data;
397
  dt_iop_graduatednd_params_t *p = (dt_iop_graduatednd_params_t *)self->params;
alicvb's avatar
alicvb committed
398 399 400

  float wd = dev->preview_pipe->backbuf_width;
  float ht = dev->preview_pipe->backbuf_height;
401 402 403 404
  float zoom_y = dt_control_get_dev_zoom_y();
  float zoom_x = dt_control_get_dev_zoom_x();
  dt_dev_zoom_t zoom = dt_control_get_dev_zoom();
  int closeup = dt_control_get_dev_closeup();
alicvb's avatar
alicvb committed
405 406
  float zoom_scale = dt_dev_get_zoom_scale(dev, zoom, closeup ? 2 : 1, 1);

407
  cairo_translate(cr, width / 2.0, height / 2.0f);
alicvb's avatar
alicvb committed
408
  cairo_scale(cr, zoom_scale, zoom_scale);
409
  cairo_translate(cr, -.5f * wd - zoom_x * wd, -.5f * ht - zoom_y * ht);
Tobias Ellinghaus's avatar
Tobias Ellinghaus committed
410

411 412
  // we get the extremities of the line
  if(g->define == 0)
alicvb's avatar
alicvb committed
413
  {
414
    if(!set_points_from_grad(self, &g->xa, &g->ya, &g->xb, &g->yb, p->rotation, p->offset)) return;
alicvb's avatar
alicvb committed
415 416
    g->define = 1;
  }
Tobias Ellinghaus's avatar
Tobias Ellinghaus committed
417

418 419 420 421 422 423 424
  float xa = g->xa * wd, xb = g->xb * wd, ya = g->ya * ht, yb = g->yb * ht;
  // the lines
  cairo_set_line_cap(cr, CAIRO_LINE_CAP_ROUND);
  if(g->selected == 3 || g->dragging == 3)
    cairo_set_line_width(cr, DT_PIXEL_APPLY_DPI(5.0) / zoom_scale);
  else
    cairo_set_line_width(cr, DT_PIXEL_APPLY_DPI(3.0) / zoom_scale);
425
  cairo_set_source_rgba(cr, .3, .3, .3, .8);
Tobias Ellinghaus's avatar
Tobias Ellinghaus committed
426

427 428
  cairo_move_to(cr, xa, ya);
  cairo_line_to(cr, xb, yb);
alicvb's avatar
alicvb committed
429
  cairo_stroke(cr);
430

431 432 433 434
  if(g->selected == 3 || g->dragging == 3)
    cairo_set_line_width(cr, DT_PIXEL_APPLY_DPI(2.0) / zoom_scale);
  else
    cairo_set_line_width(cr, DT_PIXEL_APPLY_DPI(1.0) / zoom_scale);
alicvb's avatar
alicvb committed
435
  cairo_set_source_rgba(cr, .8, .8, .8, .8);
436 437
  cairo_move_to(cr, xa, ya);
  cairo_line_to(cr, xb, yb);
alicvb's avatar
alicvb committed
438
  cairo_stroke(cr);
Tobias Ellinghaus's avatar
Tobias Ellinghaus committed
439

440 441 442 443 444 445 446 447 448 449 450 451 452
  // the extremities
  float x1, y1, x2, y2;
  float l = sqrt((xb - xa) * (xb - xa) + (yb - ya) * (yb - ya));
  const float ext = wd * 0.01f / zoom_scale;
  x1 = xa + (xb - xa) * ext / l;
  y1 = ya + (yb - ya) * ext / l;
  x2 = (xa + x1) / 2.0;
  y2 = (ya + y1) / 2.0;
  y2 += (x1 - xa);
  x2 -= (y1 - ya);
  cairo_move_to(cr, xa, ya);
  cairo_line_to(cr, x1, y1);
  cairo_line_to(cr, x2, y2);
alicvb's avatar
alicvb committed
453
  cairo_close_path(cr);
454 455 456 457 458
  cairo_set_line_width(cr, DT_PIXEL_APPLY_DPI(1.0) / zoom_scale);
  if(g->selected == 1 || g->dragging == 1)
    cairo_set_source_rgba(cr, .8, .8, .8, 1.0);
  else
    cairo_set_source_rgba(cr, .8, .8, .8, .5);
alicvb's avatar
alicvb committed
459
  cairo_fill_preserve(cr);
460 461 462 463
  if(g->selected == 1 || g->dragging == 1)
    cairo_set_source_rgba(cr, .3, .3, .3, 1.0);
  else
    cairo_set_source_rgba(cr, .3, .3, .3, .5);
alicvb's avatar
alicvb committed
464
  cairo_stroke(cr);
Tobias Ellinghaus's avatar
Tobias Ellinghaus committed
465

466 467 468 469 470 471 472 473 474
  x1 = xb - (xb - xa) * ext / l;
  y1 = yb - (yb - ya) * ext / l;
  x2 = (xb + x1) / 2.0;
  y2 = (yb + y1) / 2.0;
  y2 += (xb - x1);
  x2 -= (yb - y1);
  cairo_move_to(cr, xb, yb);
  cairo_line_to(cr, x1, y1);
  cairo_line_to(cr, x2, y2);
alicvb's avatar
alicvb committed
475
  cairo_close_path(cr);
476 477 478 479 480
  cairo_set_line_width(cr, DT_PIXEL_APPLY_DPI(1.0) / zoom_scale);
  if(g->selected == 2 || g->dragging == 2)
    cairo_set_source_rgba(cr, .8, .8, .8, 1.0);
  else
    cairo_set_source_rgba(cr, .8, .8, .8, .5);
alicvb's avatar
alicvb committed
481
  cairo_fill_preserve(cr);
482 483 484 485
  if(g->selected == 2 || g->dragging == 2)
    cairo_set_source_rgba(cr, .3, .3, .3, 1.0);
  else
    cairo_set_source_rgba(cr, .3, .3, .3, .5);
alicvb's avatar
alicvb committed
486
  cairo_stroke(cr);
487 488
}

489
int mouse_moved(struct dt_iop_module_t *self, double x, double y, double pressure, int which)
490
{
alicvb's avatar
alicvb committed
491
  dt_iop_graduatednd_gui_data_t *g = (dt_iop_graduatednd_gui_data_t *)self->gui_data;
492 493
  dt_dev_zoom_t zoom = dt_control_get_dev_zoom();
  int closeup = dt_control_get_dev_closeup();
alicvb's avatar
alicvb committed
494 495 496 497 498
  float zoom_scale = dt_dev_get_zoom_scale(self->dev, zoom, closeup ? 2 : 1, 1);
  float pzx, pzy;
  dt_dev_get_pointer_zoom_pos(self->dev, x, y, &pzx, &pzy);
  pzx += 0.5f;
  pzy += 0.5f;
Tobias Ellinghaus's avatar
Tobias Ellinghaus committed
499

500 501
  // are we dragging something ?
  if(g->dragging > 0)
alicvb's avatar
alicvb committed
502
  {
503
    if(g->dragging == 1)
alicvb's avatar
alicvb committed
504
    {
505
      // we are dragging xa,ya
alicvb's avatar
alicvb committed
506 507
      g->xa = pzx;
      g->ya = pzy;
alicvb's avatar
alicvb committed
508
    }
509
    else if(g->dragging == 2)
alicvb's avatar
alicvb committed
510
    {
511
      // we are dragging xb,yb
alicvb's avatar
alicvb committed
512 513 514
      g->xb = pzx;
      g->yb = pzy;
    }
515
    else if(g->dragging == 3)
alicvb's avatar
alicvb committed
516
    {
517 518 519 520 521
      // we are dragging the entire line
      g->xa += pzx - g->oldx;
      g->xb += pzx - g->oldx;
      g->ya += pzy - g->oldy;
      g->yb += pzy - g->oldy;
alicvb's avatar
alicvb committed
522 523
      g->oldx = pzx;
      g->oldy = pzy;
alicvb's avatar
alicvb committed
524
    }
525
  }
alicvb's avatar
alicvb committed
526 527 528
  else
  {
    g->selected = 0;
529
    const float ext = DT_PIXEL_APPLY_DPI(0.02f) / zoom_scale;
530 531
    // are we near extermity ?
    if(pzy > g->ya - ext && pzy < g->ya + ext && pzx > g->xa - ext && pzx < g->xa + ext)
alicvb's avatar
alicvb committed
532 533 534
    {
      g->selected = 1;
    }
535
    else if(pzy > g->yb - ext && pzy < g->yb + ext && pzx > g->xb - ext && pzx < g->xb + ext)
alicvb's avatar
alicvb committed
536 537 538
    {
      g->selected = 2;
    }
539 540
    else if(dist_seg(g->xa, g->ya, g->xb, g->yb, pzx, pzy) < ext * ext * 0.5)
      g->selected = 3;
alicvb's avatar
alicvb committed
541 542
  }

543
  dt_control_queue_redraw_center();
544
  return 1;
545 546
}

547 548
int button_pressed(struct dt_iop_module_t *self, double x, double y, double pressure, int which, int type,
                   uint32_t state)
549
{
alicvb's avatar
alicvb committed
550
  dt_iop_graduatednd_gui_data_t *g = (dt_iop_graduatednd_gui_data_t *)self->gui_data;
alicvb's avatar
alicvb committed
551 552 553 554
  float pzx, pzy;
  dt_dev_get_pointer_zoom_pos(self->dev, x, y, &pzx, &pzy);
  pzx += 0.5f;
  pzy += 0.5f;
Tobias Ellinghaus's avatar
Tobias Ellinghaus committed
555

556
  if(which == 3)
Tobias Ellinghaus's avatar
Tobias Ellinghaus committed
557
  {
alicvb's avatar
alicvb committed
558 559 560 561 562
    g->dragging = 2;
    g->xa = pzx;
    g->ya = pzy;
    g->xb = pzx;
    g->yb = pzy;
563 564
    g->oldx = pzx;
    g->oldy = pzy;
alicvb's avatar
alicvb committed
565
    return 1;
566
  }
567
  else if(g->selected > 0 && which == 1)
alicvb's avatar
alicvb committed
568 569
  {
    g->dragging = g->selected;
570 571
    g->oldx = pzx;
    g->oldy = pzy;
572
    return 1;
alicvb's avatar
alicvb committed
573 574
  }
  g->dragging = 0;
575 576 577 578 579
  return 0;
}

int button_released(struct dt_iop_module_t *self, double x, double y, int which, uint32_t state)
{
alicvb's avatar
alicvb committed
580
  dt_iop_graduatednd_gui_data_t *g = (dt_iop_graduatednd_gui_data_t *)self->gui_data;
581 582
  dt_iop_graduatednd_params_t *p = (dt_iop_graduatednd_params_t *)self->params;
  if(g->dragging > 0)
583
  {
584 585 586
    // dt_iop_graduatednd_params_t *p   = (dt_iop_graduatednd_params_t *)self->params;
    // float wd = self->dev->preview_pipe->backbuf_width;
    // float ht = self->dev->preview_pipe->backbuf_height;
587 588 589 590
    float pzx, pzy;
    dt_dev_get_pointer_zoom_pos(self->dev, x, y, &pzx, &pzy);
    pzx += 0.5f;
    pzy += 0.5f;
Tobias Ellinghaus's avatar
Tobias Ellinghaus committed
591

592 593 594 595
    float r = 0.0, o = 0.0;
    // float pts[4];
    // dt_dev_distort_backtransform(self->dev,pts,2);
    set_grad_from_points(self, g->xa, g->ya, g->xb, g->yb, &r, &o);
Tobias Ellinghaus's avatar
Tobias Ellinghaus committed
596

597
    // if this is a "line dragging, we reset extremities, to be sure they are not outside the image
598 599 600 601 602 603 604 605 606
    if(g->dragging == 3)
    {
      /*
       * whole line dragging should not change rotation, so we should reuse
       * old rotation to avoid rounding issues
       */
      r = p->rotation;
      set_points_from_grad(self, &g->xa, &g->ya, &g->xb, &g->yb, r, o);
    }
alicvb's avatar
alicvb committed
607
    self->dt->gui->reset = 1;
608 609
    dt_bauhaus_slider_set(g->scale3, r);
    // dt_bauhaus_slider_set(g->scale4,o);
alicvb's avatar
alicvb committed
610 611
    self->dt->gui->reset = 0;
    p->rotation = r;
Tobias Ellinghaus's avatar
Tobias Ellinghaus committed
612
    p->offset = o;
alicvb's avatar
alicvb committed
613 614
    g->dragging = 0;
    dt_dev_add_history_item(darktable.develop, self, TRUE);
615
  }
Tobias Ellinghaus's avatar
Tobias Ellinghaus committed
616

alicvb's avatar
alicvb committed
617 618 619 620 621 622 623
  g->dragging = 0;
  return 0;
}

int scrolled(dt_iop_module_t *self, double x, double y, int up, uint32_t state)
{
  dt_iop_graduatednd_gui_data_t *g = (dt_iop_graduatednd_gui_data_t *)self->gui_data;
624 625
  dt_iop_graduatednd_params_t *p = (dt_iop_graduatednd_params_t *)self->params;
  if((state & GDK_CONTROL_MASK) == GDK_CONTROL_MASK)
alicvb's avatar
alicvb committed
626 627
  {
    float dens;
628 629 630 631 632
    if(up)
      dens = fminf(8.0, p->density + 0.1);
    else
      dens = fmaxf(-8.0, p->density - 0.1);
    if(dens != p->density)
alicvb's avatar
alicvb committed
633
    {
634
      dt_bauhaus_slider_set(g->scale1, dens);
alicvb's avatar
alicvb committed
635
    }
636
    return 1;
alicvb's avatar
alicvb committed
637
  }
638
  if((state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK)
alicvb's avatar
alicvb committed
639 640
  {
    float comp;
641 642 643 644 645
    if(up)
      comp = fminf(100.0, p->compression + 1.0);
    else
      comp = fmaxf(0.0, p->compression - 1.0);
    if(comp != p->compression)
alicvb's avatar
alicvb committed
646
    {
647
      dt_bauhaus_slider_set(g->scale2, comp);
alicvb's avatar
alicvb committed
648 649 650
    }
    return 1;
  }
651 652
  return 0;
}
653

654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686
void process(struct dt_iop_module_t *self, dt_dev_pixelpipe_iop_t *piece, const void *const ivoid,
             void *const ovoid, const dt_iop_roi_t *const roi_in, const dt_iop_roi_t *const roi_out)
{
  const dt_iop_graduatednd_data_t *const data = (const dt_iop_graduatednd_data_t *const)piece->data;
  const int ch = piece->colors;

  const int ix = (roi_in->x);
  const int iy = (roi_in->y);
  const float iw = piece->buf_in.width * roi_out->scale;
  const float ih = piece->buf_in.height * roi_out->scale;
  const float hw = iw / 2.0;
  const float hh = ih / 2.0;
  const float hw_inv = 1.0 / hw;
  const float hh_inv = 1.0 / hh;
  const float v = (-data->rotation / 180) * M_PI;
  const float sinv = sin(v);
  const float cosv = cos(v);
  const float filter_radie = sqrt((hh * hh) + (hw * hw)) / hh;
  const float offset = data->offset / 100.0 * 2;

#if 1
  const float filter_compression
      = 1.0 / filter_radie / (1.0 - (0.5 + (data->compression / 100.0) * 0.9 / 2.0)) * 0.5;
#else
  const float compression = data->compression / 100.0f;
  const float t = 1.0f - .8f / (.8f + compression);
  const float c = 1.0f + 1000.0f * powf(4.0, compression);
#endif


  if(data->density > 0)
  {
#ifdef _OPENMP
687
#pragma omp parallel for default(none) schedule(static)
688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722
#endif
    for(int y = 0; y < roi_out->height; y++)
    {
      size_t k = (size_t)roi_out->width * y * ch;
      const float *in = (float *)ivoid + k;
      float *out = (float *)ovoid + k;

      float length = (sinv * (-1.0 + ix * hw_inv) - cosv * (-1.0 + (iy + y) * hh_inv) - 1.0 + offset)
                     * filter_compression;
      const float length_inc = sinv * hw_inv * filter_compression;

      for(int x = 0; x < roi_out->width; x++, in += ch, out += ch)
      {
#if 1
        // !!! approximation is ok only when highest density is 8
        // for input x = (data->density * CLIP( 0.5+length ), calculate 2^x as (e^(ln2*x/8))^8
        // use exp2f approximation to calculate e^(ln2*x/8)
        // in worst case - density==8,CLIP(0.5-length) == 1.0 it gives 0.6% of error
        const float t = 0.693147181f /* ln2 */ * (data->density * CLIP(0.5f + length) / 8.0f);
        float d1 = t * t * 0.5f;
        float d2 = d1 * t * 0.333333333f;
        float d3 = d2 * t * 0.25f;
        float d = 1 + t + d1 + d2 + d3; /* taylor series for e^x till x^4 */
        // printf("%d %d  %f\n",y,x,d);
        float density = d;
        density = density * density;
        density = density * density;
        density = density * density;
#else
        // use fair exp2f
        float density = exp2f(data->density * CLIP(0.5f + length));
#endif

        for(int l = 0; l < 3; l++)
        {
723
          out[l] = MAX(0.0f, (in[l] / (data->color[l] + data->color1[l] * density)));
724 725 726 727 728 729 730 731 732
        }

        length += length_inc;
      }
    }
  }
  else
  {
#ifdef _OPENMP
733
#pragma omp parallel for default(none) schedule(static)
734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766
#endif
    for(int y = 0; y < roi_out->height; y++)
    {
      size_t k = (size_t)roi_out->width * y * ch;
      const float *in = (float *)ivoid + k;
      float *out = (float *)ovoid + k;

      float length = (sinv * (-1.0f + ix * hw_inv) - cosv * (-1.0f + (iy + y) * hh_inv) - 1.0f + offset)
                     * filter_compression;
      const float length_inc = sinv * hw_inv * filter_compression;

      for(int x = 0; x < roi_out->width; x++, in += ch, out += ch)
      {
#if 1
        // !!! approximation is ok only when lowest density is -8
        // for input x = (-data->density * CLIP( 0.5-length ), calculate 2^x as (e^(ln2*x/8))^8
        // use exp2f approximation to calculate e^(ln2*x/8)
        // in worst case - density==-8,CLIP(0.5-length) == 1.0 it gives 0.6% of error
        const float t = 0.693147181f /* ln2 */ * (-data->density * CLIP(0.5f - length) / 8.0f);
        float d1 = t * t * 0.5f;
        float d2 = d1 * t * 0.333333333f;
        float d3 = d2 * t * 0.25f;
        float d = 1 + t + d1 + d2 + d3; /* taylor series for e^x till x^4 */
        float density = d;
        density = density * density;
        density = density * density;
        density = density * density;
#else
        float density = exp2f(-data->density * CLIP(0.5f - length));
#endif

        for(int l = 0; l < 3; l++)
        {
767
          out[l] = MAX(0.0f, (in[l] * (data->color[l] + data->color1[l] * density)));
768 769 770 771 772 773 774
        }

        length += length_inc;
      }
    }
  }

775
  if(piece->pipe->mask_display & DT_DEV_PIXELPIPE_DISPLAY_MASK) dt_iop_alpha_copy(ivoid, ovoid, roi_out->width, roi_out->height);
776 777
}

778
#if defined(__SSE__)
779 780
void process_sse2(struct dt_iop_module_t *self, dt_dev_pixelpipe_iop_t *piece, const void *const ivoid,
                  void *const ovoid, const dt_iop_roi_t *const roi_in, const dt_iop_roi_t *const roi_out)
781
{
782
  const dt_iop_graduatednd_data_t *const data = (const dt_iop_graduatednd_data_t *const)piece->data;
783
  const int ch = piece->colors;
784

785 786 787 788 789 790 791 792 793 794 795 796 797
  const int ix = (roi_in->x);
  const int iy = (roi_in->y);
  const float iw = piece->buf_in.width * roi_out->scale;
  const float ih = piece->buf_in.height * roi_out->scale;
  const float hw = iw / 2.0;
  const float hh = ih / 2.0;
  const float hw_inv = 1.0 / hw;
  const float hh_inv = 1.0 / hh;
  const float v = (-data->rotation / 180) * M_PI;
  const float sinv = sin(v);
  const float cosv = cos(v);
  const float filter_radie = sqrt((hh * hh) + (hw * hw)) / hh;
  const float offset = data->offset / 100.0 * 2;
Henrik Andersson's avatar
Henrik Andersson committed
798

799
#if 1
800 801
  const float filter_compression = 1.0 / filter_radie
                                   / (1.0 - (0.5 + (data->compression / 100.0) * 0.9 / 2.0)) * 0.5;
802
#else
803 804 805
  const float compression = data->compression / 100.0f;
  const float t = 1.0f - .8f / (.8f + compression);
  const float c = 1.0f + 1000.0f * powf(4.0, compression);
806
#endif
807

Tobias Ellinghaus's avatar
Tobias Ellinghaus committed
808

809
  if(data->density > 0)
810
  {
811
#ifdef _OPENMP
812
#pragma omp parallel for default(none) schedule(static)
813
#endif
814
    for(int y = 0; y < roi_out->height; y++)
815
    {
816 817 818
      size_t k = (size_t)roi_out->width * y * ch;
      const float *in = (float *)ivoid + k;
      float *out = (float *)ovoid + k;
819

820 821
      float length = (sinv * (-1.0 + ix * hw_inv) - cosv * (-1.0 + (iy + y) * hh_inv) - 1.0 + offset)
                     * filter_compression;
822
      const float length_inc = sinv * hw_inv * filter_compression;
823

824
      __m128 c = _mm_set_ps(0, data->color[2], data->color[1], data->color[0]);
825
      __m128 c1 = _mm_sub_ps(_mm_set1_ps(1.0f), c);
826

827
      for(int x = 0; x < roi_out->width; x++, in += ch, out += ch)
828
      {
Henrik Andersson's avatar
Henrik Andersson committed
829
#if 1
830 831 832 833
        // !!! approximation is ok only when highest density is 8
        // for input x = (data->density * CLIP( 0.5+length ), calculate 2^x as (e^(ln2*x/8))^8
        // use exp2f approximation to calculate e^(ln2*x/8)
        // in worst case - density==8,CLIP(0.5-length) == 1.0 it gives 0.6% of error
834 835 836 837 838 839
        const float t = 0.693147181f /* ln2 */ * (data->density * CLIP(0.5f + length) / 8.0f);
        float d1 = t * t * 0.5f;
        float d2 = d1 * t * 0.333333333f;
        float d3 = d2 * t * 0.25f;
        float d = 1 + t + d1 + d2 + d3; /* taylor series for e^x till x^4 */
        // printf("%d %d  %f\n",y,x,d);
840
        __m128 density = _mm_set1_ps(d);
841 842 843
        density = _mm_mul_ps(density, density);
        density = _mm_mul_ps(density, density);
        density = _mm_mul_ps(density, density);
johannes hanika's avatar
johannes hanika committed
844
#else
845
        // use fair exp2f
846
        __m128 density = _mm_set1_ps(exp2f(data->density * CLIP(0.5f + length)));
johannes hanika's avatar
johannes hanika committed
847
#endif
848 849

        /* max(0,in / (c + (1-c)*density)) */
850 851
        _mm_stream_ps(out, _mm_max_ps(_mm_set1_ps(0.0f),
                                      _mm_div_ps(_mm_load_ps(in), _mm_add_ps(c, _mm_mul_ps(c1, density)))));
852 853

        length += length_inc;
854
      }
855 856 857 858 859
    }
  }
  else
  {
#ifdef _OPENMP
860
#pragma omp parallel for default(none) schedule(static)
johannes hanika's avatar
johannes hanika committed
861
#endif
862
    for(int y = 0; y < roi_out->height; y++)
863
    {
864 865 866
      size_t k = (size_t)roi_out->width * y * ch;
      const float *in = (float *)ivoid + k;
      float *out = (float *)ovoid + k;
867

868 869
      float length = (sinv * (-1.0f + ix * hw_inv) - cosv * (-1.0f + (iy + y) * hh_inv) - 1.0f + offset)
                     * filter_compression;
870
      const float length_inc = sinv * hw_inv * filter_compression;
871

872
      __m128 c = _mm_set_ps(0, data->color[2], data->color[1], data->color[0]);
873
      __m128 c1 = _mm_sub_ps(_mm_set1_ps(1.0f), c);
874

875
      for(int x = 0; x < roi_out->width; x++, in += ch, out += ch)
876
      {
877
#if 1
878 879 880 881
        // !!! approximation is ok only when lowest density is -8
        // for input x = (-data->density * CLIP( 0.5-length ), calculate 2^x as (e^(ln2*x/8))^8
        // use exp2f approximation to calculate e^(ln2*x/8)
        // in worst case - density==-8,CLIP(0.5-length) == 1.0 it gives 0.6% of error
882 883 884 885 886
        const float t = 0.693147181f /* ln2 */ * (-data->density * CLIP(0.5f - length) / 8.0f);
        float d1 = t * t * 0.5f;
        float d2 = d1 * t * 0.333333333f;
        float d3 = d2 * t * 0.25f;
        float d = 1 + t + d1 + d2 + d3; /* taylor series for e^x till x^4 */
887
        __m128 density = _mm_set1_ps(d);
888 889 890
        density = _mm_mul_ps(density, density);
        density = _mm_mul_ps(density, density);
        density = _mm_mul_ps(density, density);
891
#else
892
        __m128 density = _mm_set1_ps(exp2f(-data->density * CLIP(0.5f - length)));
893
#endif
894

895
        /* max(0,in * (c + (1-c)*density)) */
896 897
        _mm_stream_ps(out, _mm_max_ps(_mm_set1_ps(0.0f),
                                      _mm_mul_ps(_mm_load_ps(in), _mm_add_ps(c, _mm_mul_ps(c1, density)))));
898 899

        length += length_inc;
900
      }
901
    }
902
  }
903
  _mm_sfence();
904

905
  if(piece->pipe->mask_display & DT_DEV_PIXELPIPE_DISPLAY_MASK) dt_iop_alpha_copy(ivoid, ovoid, roi_out->width, roi_out->height);
906
}
907
#endif
908

909 910

#ifdef HAVE_OPENCL
911
int process_cl(struct dt_iop_module_t *self, dt_dev_pixelpipe_iop_t *piece, cl_mem dev_in, cl_mem dev_out,
912
               const dt_iop_roi_t *const roi_in, const dt_iop_roi_t *const roi_out)
913 914 915 916 917 918 919 920 921
{
  dt_iop_graduatednd_data_t *data = (dt_iop_graduatednd_data_t *)piece->data;
  dt_iop_graduatednd_global_data_t *gd = (dt_iop_graduatednd_global_data_t *)self->data;

  cl_int err = -999;
  const int devid = piece->pipe->devid;
  const int width = roi_in->width;
  const int height = roi_in->height;

922 923 924 925 926 927 928 929 930 931 932 933 934 935
  const int ix = (roi_in->x);
  const int iy = (roi_in->y);
  const float iw = piece->buf_in.width * roi_out->scale;
  const float ih = piece->buf_in.height * roi_out->scale;
  const float hw = iw / 2.0;
  const float hh = ih / 2.0;
  const float hw_inv = 1.0 / hw;
  const float hh_inv = 1.0 / hh;
  const float v = (-data->rotation / 180) * M_PI;
  const float sinv = sin(v);
  const float cosv = cos(v);
  const float filter_radie = sqrt((hh * hh) + (hw * hw)) / hh;
  const float offset = data->offset / 100.0 * 2;
  const float density = data->density;
936 937

#if 1
938 939
  const float filter_compression = 1.0 / filter_radie
                                   / (1.0 - (0.5 + (data->compression / 100.0) * 0.9 / 2.0)) * 0.5;
940
#else
941 942 943
  const float compression = data->compression / 100.0f;
  const float t = 1.0f - .8f / (.8f + compression);
  const float c = 1.0f + 1000.0f * powf(4.0, compression);
944 945
#endif

946 947
  const float length_base = (sinv * (-1.0 + ix * hw_inv) - cosv * (-1.0 + iy * hh_inv) - 1.0 + offset)
                            * filter_compression;
948
  const float length_inc_y = -cosv * hh_inv * filter_compression;
949
  const float length_inc_x = sinv * hw_inv * filter_compression;
950

951
  size_t sizes[] = { ROUNDUPWD(width), ROUNDUPHT(height), 1 };
952 953 954 955 956 957 958

  int kernel = density > 0 ? gd->kernel_graduatedndp : gd->kernel_graduatedndm;

  dt_opencl_set_kernel_arg(devid, kernel, 0, sizeof(cl_mem), (void *)&dev_in);
  dt_opencl_set_kernel_arg(devid, kernel, 1, sizeof(cl_mem), (void *)&dev_out);
  dt_opencl_set_kernel_arg(devid, kernel, 2, sizeof(int), (void *)&width);
  dt_opencl_set_kernel_arg(devid, kernel, 3, sizeof(int), (void *)&height);
959
  dt_opencl_set_kernel_arg(devid, kernel, 4, 4 * sizeof(float), (void *)data->color);
960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976
  dt_opencl_set_kernel_arg(devid, kernel, 5, sizeof(float), (void *)&density);
  dt_opencl_set_kernel_arg(devid, kernel, 6, sizeof(float), (void *)&length_base);
  dt_opencl_set_kernel_arg(devid, kernel, 7, sizeof(float), (void *)&length_inc_x);
  dt_opencl_set_kernel_arg(devid, kernel, 8, sizeof(float), (void *)&length_inc_y);
  err = dt_opencl_enqueue_kernel_2d(devid, kernel, sizes);
  if(err != CL_SUCCESS) goto error;
  return TRUE;

error:
  dt_print(DT_DEBUG_OPENCL, "[opencl_graduatednd] couldn't enqueue kernel! %d\n", err);
  return FALSE;
}
#endif

void init_global(dt_iop_module_so_t *module)
{
  const int program = 8; // extended.cl, from programs.conf
977 978
  dt_iop_graduatednd_global_data_t *gd
      = (dt_iop_graduatednd_global_data_t *)malloc(sizeof(dt_iop_graduatednd_global_data_t));
979 980 981 982 983 984 985 986 987 988 989 990 991 992 993
  module->data = gd;
  gd->kernel_graduatedndp = dt_opencl_create_kernel(program, "graduatedndp");
  gd->kernel_graduatedndm = dt_opencl_create_kernel(program, "graduatedndm");
}

void cleanup_global(dt_iop_module_so_t *module)
{
  dt_iop_graduatednd_global_data_t *gd = (dt_iop_graduatednd_global_data_t *)module->data;
  dt_opencl_free_kernel(gd->kernel_graduatedndp);
  dt_opencl_free_kernel(gd->kernel_graduatedndm);
  free(module->data);
  module->data = NULL;
}


994
static void density_callback(GtkWidget *slider, gpointer user_data)
995 996 997 998
{
  dt_iop_module_t *self = (dt_iop_module_t *)user_data;
  if(self->dt->gui->reset) return;
  dt_iop_graduatednd_params_t *p = (dt_iop_graduatednd_params_t *)self->params;
999
  p->density = dt_bauhaus_slider_get(slider);
1000
  dt_dev_add_history_item(darktable.develop, self, TRUE);
1001 1002
}

1003
static void compression_callback(GtkWidget *slider, gpointer user_data)
1004 1005 1006 1007
{
  dt_iop_module_t *self = (dt_iop_module_t *)user_data;
  if(self->dt->gui->reset) return;
  dt_iop_graduatednd_params_t *p = (dt_iop_graduatednd_params_t *)self->params;
1008
  p->compression = dt_bauhaus_slider_get(slider);
1009
  dt_dev_add_history_item(darktable.develop, self, TRUE);
1010 1011
}

1012
static void rotation_callback(GtkWidget *slider, gpointer user_data)
1013 1014 1015 1016
{
  dt_iop_module_t *self = (dt_iop_module_t *)user_data;
  if(self->dt->gui->reset) return;
  dt_iop_graduatednd_params_t *p = (dt_iop_graduatednd_params_t *)self->params;
alicvb's avatar
alicvb committed
1017
  dt_iop_graduatednd_gui_data_t *g = (dt_iop_graduatednd_gui_data_t *)self->gui_data;
1018 1019 1020 1021
  // float wd = self->dev->preview_pipe->backbuf_width;
  // float ht = self->dev->preview_pipe->backbuf_height;
  p->rotation = dt_bauhaus_slider_get(slider);
  set_points_from_grad(self, &g->xa, &g->ya, &g->xb, &g->yb, p->rotation, p->offset);
1022 1023 1024
  dt_dev_add_history_item(darktable.develop, self, TRUE);
}

1025 1026
void commit_params(struct dt_iop_module_t *self, dt_iop_params_t *p1, dt_dev_pixelpipe_t *pipe,
                   dt_dev_pixelpipe_iop_t *piece)
1027 1028 1029
{
  dt_iop_graduatednd_params_t *p = (dt_iop_graduatednd_params_t *)p1;
  dt_iop_graduatednd_data_t *d = (dt_iop_graduatednd_data_t *)piece->data;
1030

1031 1032 1033 1034
  d->density = p->density;
  d->compression = p->compression;
  d->rotation = p->rotation;
  d->offset = p->offset;
1035 1036 1037 1038 1039 1040 1041 1042

  hsl2rgb(d->color, p->hue, p->saturation, 0.5);
  d->color[3] = 0.0f;

  if(d->density < 0)
    for(int l = 0; l < 4; l++) d->color[l] = 1.0 - d->color[l];

  for(int l = 0; l < 4; l++) d->color1[l] = 1.0 - d->color[l];
1043 1044
}

1045
void init_pipe(struct dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
1046
{
1047
  piece->data = calloc(1, sizeof(dt_iop_graduatednd_data_t));
1048 1049 1050
  self->commit_params(self, self->default_params, pipe, piece);
}

1051
void cleanup_pipe(struct dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
1052 1053
{
  free(piece->data);
1054
  piece->data = NULL;
1055 1056
}

1057
static inline void update_saturation_slider_end_color(GtkWidget *slider, float hue)
1058 1059 1060 1061 1062 1063
{
  float rgb[3];
  hsl2rgb(rgb, hue, 1.0, 0.5);
  dt_bauhaus_slider_set_stop(slider, 1.0, rgb[0], rgb[1], rgb[2]);
}

1064 1065 1066 1067 1068
void gui_update(struct dt_iop_module_t *self)
{
  dt_iop_module_t *module = (dt_iop_module_t *)self;
  dt_iop_graduatednd_gui_data_t *g = (dt_iop_graduatednd_gui_data_t *)self->gui_data;
  dt_iop_graduatednd_params_t *p = (dt_iop_graduatednd_params_t *)module->params;
1069 1070
  dt_bauhaus_slider_set(g->scale1, p->density);
  dt_bauhaus_slider_set(g->scale2, p->compression);
1071
  dt_bauhaus_slider_set(g->scale3, p->rotation);
1072 1073
  dt_bauhaus_slider_set(g->gslider1, p->hue);
  dt_bauhaus_slider_set(g->gslider2, p->saturation);
Tobias Ellinghaus's avatar
Tobias Ellinghaus committed
1074

1075 1076
  // float wd = self->dev->preview_pipe->backbuf_width;
  // float ht = self->dev->preview_pipe->backbuf_height;
1077
  g->define = 0;
1078
  // set_points_from_grad(self,&g->xa,&g->ya,&g->xb,&g->yb,p->rotation,p->offset);
1079
  update_saturation_slider_end_color(g->gslider2, p->hue);
1080 1081 1082 1083
}

void init(dt_iop_module_t *module)
{
1084 1085
  module->params = calloc(1, sizeof(dt_iop_graduatednd_params_t));
  module->default_params = calloc(1, sizeof(dt_iop_graduatednd_params_t));
1086
  module->default_enabled = 0;
Heiko Bauke's avatar
Heiko Bauke committed
1087
  module->priority = 279; // module order created by iop_dependencies.py, do not edit!
1088 1089
  module->params_size = sizeof(dt_iop_graduatednd_params_t);
  module->gui_data = NULL;
1090
  dt_iop_graduatednd_params_t tmp = (dt_iop_graduatednd_params_t){ 1.0, 0, 0, 50, 0, 0 };
1091 1092 1093 1094 1095 1096 1097 1098 1099 1100
  memcpy(module->params, &tmp, sizeof(dt_iop_graduatednd_params_t));
  memcpy(module->default_params, &tmp, sizeof(dt_iop_graduatednd_params_t));
}

void cleanup(dt_iop_module_t *module)
{
  free(module->params);
  module->params = NULL;
}

1101
static void hue_callback(GtkWidget *slider, gpointer user_data)
Henrik Andersson's avatar
Henrik Andersson committed
1102 1103 1104 1105 1106
{
  dt_iop_module_t *self = (dt_iop_module_t *)user_data;
  dt_iop_graduatednd_params_t *p = (dt_iop_graduatednd_params_t *)self->params;
  dt_iop_graduatednd_gui_data_t *g = (dt_iop_graduatednd_gui_data_t *)self->gui_data;

1107
  const float hue = dt_bauhaus_slider_get(g->gslider1);
1108
  // fprintf(stderr," hue: %f, saturation: %f\n",hue,dtgtk_gradient_slider_get_value(g->gslider2));
1109

1110
  update_saturation_slider_end_color(g->gslider2, hue);
Henrik Andersson's avatar
Henrik Andersson committed
1111

1112
  if(self->dt->gui->reset) return;
1113
  gtk_widget_queue_draw(GTK_WIDGET(g->gslider2));
1114

1115
  p->hue = hue;
1116
  dt_dev_add_history_item(darktable.develop, self, TRUE);
Henrik Andersson's avatar
Henrik Andersson committed
1117 1118
}

1119
static void saturation_callback(GtkWidget *slider, gpointer user_data)
Henrik Andersson's avatar
Henrik Andersson committed
1120 1121 1122
{
  dt_iop_module_t *self = (dt_iop_module_t *)user_data;
  dt_iop_graduatednd_params_t *p = (dt_iop_graduatednd_params_t *)self->params;
1123

1124
  p->saturation = dt_bauhaus_slider_get(slider);
1125
  dt_dev_add_history_item(darktable.develop, self, TRUE);
Henrik Andersson's avatar
Henrik Andersson committed
1126 1127 1128
}


1129 1130 1131 1132 1133 1134
void gui_init(struct dt_iop_module_t *self)
{
  self->gui_data = malloc(sizeof(dt_iop_graduatednd_gui_data_t));
  dt_iop_graduatednd_gui_data_t *g = (dt_iop_graduatednd_gui_data_t *)self->gui_data;
  dt_iop_graduatednd_params_t *p = (dt_iop_graduatednd_params_t *)self->params;

1135
  self->widget = gtk_box_new(GTK_ORIENTATION_VERTICAL, DT_BAUHAUS_SPACE);
1136