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

    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 27 28 29
#include "bauhaus/bauhaus.h"
#include "common/opencl.h"
#include "control/control.h"
#include "develop/blend.h"
30 31
#include "develop/develop.h"
#include "develop/imageop.h"
32
#include "dtgtk/resetlabel.h"
33
#include "gui/accelerators.h"
34
#include "gui/gtk.h"
35 36
#include "gui/presets.h"
#include "iop/iop_api.h"
37 38 39
#include <gtk/gtk.h>
#include <inttypes.h>

40
DT_MODULE_INTROSPECTION(4, dt_iop_vignette_params_t)
41

42
#define CLIP(x) ((x < 0) ? 0.0 : (x > 1.0) ? 1.0 : x)
43 44
#define TEA_ROUNDS 8

45 46 47 48 49 50 51
typedef enum dt_iop_dither_t
{
  DITHER_OFF = 0,
  DITHER_8BIT = 1,
  DITHER_16BIT = 2
} dt_iop_dither_t;

52
typedef struct dt_iop_dvector_2d_t
53 54 55
{
  double x;
  double y;
56 57
} dt_iop_dvector_2d_t;

58 59 60 61 62 63
typedef struct dt_iop_fvector_2d_t
{
  float x;
  float y;
} dt_iop_vector_2d_t;

64
typedef struct dt_iop_vignette_params1_t
65
{
66 67 68 69 70 71
  double scale;         // 0 - 100 Radie
  double falloff_scale; // 0 - 100 Radie for falloff inner radie of falloff=scale and
                        // outer=scale+falloff_scale
  double strength;      // 0 - 1 strength of effect
  double uniformity;    // 0 - 1 uniformity of center
  double bsratio;       // -1 - +1 ratio of brightness/saturation effect
72
  gboolean invert_falloff;
73
  gboolean invert_saturation;
74 75
  dt_iop_dvector_2d_t center; // Center of vignette
} dt_iop_vignette_params1_t;
76

77 78
typedef struct dt_iop_vignette_params2_t
{
79 80 81 82 83 84 85
  float scale;               // 0 - 100 Inner radius, percent of largest image dimension
  float falloff_scale;       // 0 - 100 Radius for falloff -- outer radius = inner radius + falloff_scale
  float brightness;          // -1 - 1 Strength of brightness reduction
  float saturation;          // -1 - 1 Strength of saturation reduction
  dt_iop_vector_2d_t center; // Center of vignette
  gboolean autoratio;        //
  float whratio;             // 0-1 = width/height ratio, 1-2 = height/width ratio + 1
86
  float shape;
87
} dt_iop_vignette_params2_t;
88

89 90
typedef struct dt_iop_vignette_params3_t
{
91 92 93 94 95 96 97
  float scale;               // 0 - 100 Inner radius, percent of largest image dimension
  float falloff_scale;       // 0 - 100 Radius for falloff -- outer radius = inner radius + falloff_scale
  float brightness;          // -1 - 1 Strength of brightness reduction
  float saturation;          // -1 - 1 Strength of saturation reduction
  dt_iop_vector_2d_t center; // Center of vignette
  gboolean autoratio;        //
  float whratio;             // 0-1 = width/height ratio, 1-2 = height/width ratio + 1
98
  float shape;
99 100
  int dithering; // if and how to perform dithering
} dt_iop_vignette_params3_t;
101

102 103
typedef struct dt_iop_vignette_params_t
{
104 105 106 107 108 109 110
  float scale;               // 0 - 100 Inner radius, percent of largest image dimension
  float falloff_scale;       // 0 - 100 Radius for falloff -- outer radius = inner radius + falloff_scale
  float brightness;          // -1 - 1 Strength of brightness reduction
  float saturation;          // -1 - 1 Strength of saturation reduction
  dt_iop_vector_2d_t center; // Center of vignette
  gboolean autoratio;        //
  float whratio;             // 0-1 = width/height ratio, 1-2 = height/width ratio + 1
111
  float shape;
112 113 114
  int dithering;    // if and how to perform dithering
  gboolean unbound; // whether the values should be clipped
} dt_iop_vignette_params_t;
115

116

117 118
typedef struct dt_iop_vignette_gui_data_t
{
119 120 121 122 123 124
  GtkWidget *scale;
  GtkWidget *falloff_scale;
  GtkWidget *brightness;
  GtkWidget *saturation;
  GtkWidget *center_x;
  GtkWidget *center_y;
125
  GtkToggleButton *autoratio;
126 127
  GtkWidget *whratio;
  GtkWidget *shape;
128
  GtkWidget *dithering;
129
} dt_iop_vignette_gui_data_t;
130 131 132

typedef struct dt_iop_vignette_data_t
{
133 134 135 136
  float scale;
  float falloff_scale;
  float brightness;
  float saturation;
137
  dt_iop_vector_2d_t center; // Center of vignette
138 139 140
  gboolean autoratio;
  float whratio;
  float shape;
141
  int dithering;
142
  gboolean unbound;
143
} dt_iop_vignette_data_t;
144

145 146 147
typedef struct dt_iop_vignette_global_data_t
{
  int kernel_vignette;
148
} dt_iop_vignette_global_data_t;
149

150 151
const char *name()
{
Alexandre Prokoudine's avatar
Alexandre Prokoudine committed
152
  return _("vignetting");
153 154
}

155 156
int flags()
{
157 158
  return IOP_FLAGS_INCLUDE_IN_STYLES | IOP_FLAGS_SUPPORTS_BLENDING | IOP_FLAGS_ALLOW_TILING
         | IOP_FLAGS_TILING_FULL_ROI;
159
}
160

161
int groups()
162
{
163
  return IOP_GROUP_EFFECT;
164 165
}

166
void init_key_accels(dt_iop_module_so_t *self)
167
{
168 169 170 171 172 173 174 175
  dt_accel_register_slider_iop(self, FALSE, NC_("accel", "scale"));
  dt_accel_register_slider_iop(self, FALSE, NC_("accel", "fall-off strength"));
  dt_accel_register_slider_iop(self, FALSE, NC_("accel", "brightness"));
  dt_accel_register_slider_iop(self, FALSE, NC_("accel", "saturation"));
  dt_accel_register_slider_iop(self, FALSE, NC_("accel", "horizontal center"));
  dt_accel_register_slider_iop(self, FALSE, NC_("accel", "vertical center"));
  dt_accel_register_slider_iop(self, FALSE, NC_("accel", "shape"));
  dt_accel_register_slider_iop(self, FALSE, NC_("accel", "width-height ratio"));
176
  dt_accel_register_slider_iop(self, FALSE, NC_("accel", "dithering"));
177
}
178 179 180

void connect_key_accels(dt_iop_module_t *self)
{
181 182 183 184 185 186 187 188 189 190 191
  dt_iop_vignette_gui_data_t *g = (dt_iop_vignette_gui_data_t *)self->gui_data;

  dt_accel_connect_slider_iop(self, "scale", GTK_WIDGET(g->scale));
  dt_accel_connect_slider_iop(self, "fall-off strength", GTK_WIDGET(g->falloff_scale));
  dt_accel_connect_slider_iop(self, "brightness", GTK_WIDGET(g->brightness));
  dt_accel_connect_slider_iop(self, "saturation", GTK_WIDGET(g->saturation));
  dt_accel_connect_slider_iop(self, "horizontal center", GTK_WIDGET(g->center_x));
  dt_accel_connect_slider_iop(self, "vertical center", GTK_WIDGET(g->center_y));
  dt_accel_connect_slider_iop(self, "shape", GTK_WIDGET(g->shape));
  dt_accel_connect_slider_iop(self, "width-height ratio", GTK_WIDGET(g->whratio));
  dt_accel_connect_slider_iop(self, "dithering", GTK_WIDGET(g->dithering));
192 193
}

194 195
int legacy_params(dt_iop_module_t *self, const void *const old_params, const int old_version,
                  void *new_params, const int new_version)
196
{
197
  if(old_version == 1 && new_version == 4)
198 199 200 201 202
  {
    const dt_iop_vignette_params1_t *old = old_params;
    dt_iop_vignette_params_t *new = new_params;
    new->scale = old->scale;
    new->falloff_scale = old->falloff_scale;
203 204 205 206 207 208 209 210 211 212
    new->brightness = -(1.0 - MAX(old->bsratio, 0.0)) * old->strength / 100.0;
    new->saturation = -(1.0 + MIN(old->bsratio, 0.0)) * old->strength / 100.0;
    if(old->invert_saturation) new->saturation *= -2.0; // Double effect for increasing saturation
    if(old->invert_falloff) new->brightness = -new->brightness;
    new->center.x = old->center.x;
    new->center.y = old->center.y;
    new->autoratio = TRUE;
    new->whratio = 1.0;
    new->shape = 1.0;
    new->dithering = DITHER_OFF;
213
    new->unbound = FALSE;
214 215
    return 0;
  }
216
  if(old_version == 2 && new_version == 4)
217 218 219 220 221
  {
    const dt_iop_vignette_params2_t *old = old_params;
    dt_iop_vignette_params_t *new = new_params;
    new->scale = old->scale;
    new->falloff_scale = old->falloff_scale;
222 223 224 225 226 227 228 229
    new->brightness = old->brightness;
    new->saturation = old->saturation;
    new->center.x = old->center.x;
    new->center.y = old->center.y;
    new->autoratio = old->autoratio;
    new->whratio = old->whratio;
    new->shape = old->shape;
    new->dithering = DITHER_OFF;
230
    new->unbound = FALSE;
231 232
    return 0;
  }
233
  if(old_version == 3 && new_version == 4)
234 235 236 237 238
  {
    const dt_iop_vignette_params3_t *old = old_params;
    dt_iop_vignette_params_t *new = new_params;
    new->scale = old->scale;
    new->falloff_scale = old->falloff_scale;
239 240 241 242 243 244 245 246
    new->brightness = old->brightness;
    new->saturation = old->saturation;
    new->center.x = old->center.x;
    new->center.y = old->center.y;
    new->autoratio = old->autoratio;
    new->whratio = old->whratio;
    new->shape = old->shape;
    new->dithering = old->dithering;
247
    new->unbound = FALSE;
248 249 250
    return 0;
  }

251 252
  return 1;
}
253

254

255
static void encrypt_tea(unsigned int *arg)
256
{
257
  const unsigned int key[] = { 0xa341316c, 0xc8013ea4, 0xad90777d, 0x7e95761e };
258 259 260 261 262 263 264 265 266 267 268 269 270
  unsigned int v0 = arg[0], v1 = arg[1];
  unsigned int sum = 0;
  unsigned int delta = 0x9e3779b9;
  for(int i = 0; i < TEA_ROUNDS; i++)
  {
    sum += delta;
    v0 += ((v1 << 4) + key[0]) ^ (v1 + sum) ^ ((v1 >> 5) + key[1]);
    v1 += ((v0 << 4) + key[2]) ^ (v0 + sum) ^ ((v0 >> 5) + key[3]);
  }
  arg[0] = v0;
  arg[1] = v1;
}

271
static float tpdf(unsigned int urandom)
272 273 274
{
  float frandom = (float)urandom / 0xFFFFFFFFu;

275
  return (frandom < 0.5f ? (sqrtf(2.0f * frandom) - 1.0f) : (1.0f - sqrtf(2.0f * (1.0f - frandom))));
276 277
}

278 279
static int get_grab(float pointerx, float pointery, float startx, float starty, float endx, float endy,
                    float zoom_scale)
Henrik Andersson's avatar
Henrik Andersson committed
280
{
281
  const float radius = 5.0 / zoom_scale;
282

283 284 285 286 287
  if(powf(pointerx - startx, 2) + powf(pointery, 2) <= powf(radius, 2)) return 2; // x size
  if(powf(pointerx, 2) + powf(pointery - starty, 2) <= powf(radius, 2)) return 4; // y size
  if(powf(pointerx, 2) + powf(pointery, 2) <= powf(radius, 2)) return 1;          // center
  if(powf(pointerx - endx, 2) + powf(pointery, 2) <= powf(radius, 2)) return 8;   // x falloff
  if(powf(pointerx, 2) + powf(pointery - endy, 2) <= powf(radius, 2)) return 16;  // y falloff
288 289 290 291

  return 0;
}

292
static void draw_overlay(cairo_t *cr, float x, float y, float fx, float fy, int grab, float zoom_scale)
293
{
294
  // half width/height of the crosshair
295 296
  float crosshair_w = DT_PIXEL_APPLY_DPI(10.0) / zoom_scale;
  float crosshair_h = DT_PIXEL_APPLY_DPI(10.0) / zoom_scale;
297

298 299
  // center crosshair
  cairo_move_to(cr, -crosshair_w, 0.0);
300
  cairo_line_to(cr, crosshair_w, 0.0);
301
  cairo_move_to(cr, 0.0, -crosshair_h);
302
  cairo_line_to(cr, 0.0, crosshair_h);
303 304
  cairo_stroke(cr);

305
  // inner border of the vignette
306
  cairo_save(cr);
307 308
  if(x <= y)
  {
309 310
    cairo_scale(cr, x / y, 1.0);
    cairo_arc(cr, 0.0, 0.0, y, 0.0, M_PI * 2.0);
311 312 313
  }
  else
  {
314 315
    cairo_scale(cr, 1.0, y / x);
    cairo_arc(cr, 0.0, 0.0, x, 0.0, M_PI * 2.0);
316 317
  }
  cairo_restore(cr);
318
  cairo_stroke(cr);
319 320 321 322 323

  // outer border of the vignette
  cairo_save(cr);
  if(fx <= fy)
  {
324 325
    cairo_scale(cr, fx / fy, 1.0);
    cairo_arc(cr, 0.0, 0.0, fy, 0.0, M_PI * 2.0);
326 327 328
  }
  else
  {
329 330
    cairo_scale(cr, 1.0, fy / fx);
    cairo_arc(cr, 0.0, 0.0, fx, 0.0, M_PI * 2.0);
331
  }
332
  cairo_restore(cr);
333
  cairo_stroke(cr);
334

335
  // the handles
336 337 338 339 340 341
  const float radius_sel = DT_PIXEL_APPLY_DPI(6.0) / zoom_scale;
  const float radius_reg = DT_PIXEL_APPLY_DPI(4.0) / zoom_scale;
  if(grab == 1)
    cairo_arc(cr, 0.0, 0.0, radius_sel, 0.0, M_PI * 2.0);
  else
    cairo_arc(cr, 0.0, 0.0, radius_reg, 0.0, M_PI * 2.0);
342
  cairo_stroke(cr);
343 344 345 346
  if(grab == 2)
    cairo_arc(cr, x, 0.0, radius_sel, 0.0, M_PI * 2.0);
  else
    cairo_arc(cr, x, 0.0, radius_reg, 0.0, M_PI * 2.0);
347
  cairo_stroke(cr);
348 349 350 351
  if(grab == 4)
    cairo_arc(cr, 0.0, -y, radius_sel, 0.0, M_PI * 2.0);
  else
    cairo_arc(cr, 0.0, -y, radius_reg, 0.0, M_PI * 2.0);
352
  cairo_stroke(cr);
353 354 355 356
  if(grab == 8)
    cairo_arc(cr, fx, 0.0, radius_sel, 0.0, M_PI * 2.0);
  else
    cairo_arc(cr, fx, 0.0, radius_reg, 0.0, M_PI * 2.0);
357
  cairo_stroke(cr);
358 359 360 361
  if(grab == 16)
    cairo_arc(cr, 0.0, -fy, radius_sel, 0.0, M_PI * 2.0);
  else
    cairo_arc(cr, 0.0, -fy, radius_reg, 0.0, M_PI * 2.0);
362 363 364
  cairo_stroke(cr);
}

365 366 367 368
// FIXME: For portrait images the overlay is a bit off. The coordinates in mouse_moved seem to be ok though.
// WTF?
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)
369
{
370 371 372
  dt_develop_t *dev = self->dev;
  //   dt_iop_vignette_gui_data_t *g = (dt_iop_vignette_gui_data_t *)self->gui_data;
  dt_iop_vignette_params_t *p = (dt_iop_vignette_params_t *)self->params;
373 374 375 376

  float wd = dev->preview_pipe->backbuf_width;
  float ht = dev->preview_pipe->backbuf_height;
  float bigger_side, smaller_side;
Henrik Andersson's avatar
Henrik Andersson committed
377 378 379 380 381 382 383 384 385 386
  if(wd >= ht)
  {
    bigger_side = wd;
    smaller_side = ht;
  }
  else
  {
    bigger_side = ht;
    smaller_side = wd;
  }
387 388 389 390
  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();
391 392 393 394 395 396
  float zoom_scale = dt_dev_get_zoom_scale(dev, zoom, closeup ? 2 : 1, 1);
  float pzx, pzy;
  dt_dev_get_pointer_zoom_pos(dev, pointerx, pointery, &pzx, &pzy);
  pzx += 0.5f;
  pzy += 0.5f;

397
  cairo_translate(cr, width / 2.0, height / 2.0);
398
  cairo_scale(cr, zoom_scale, zoom_scale);
399
  cairo_translate(cr, -.5f * wd - zoom_x * wd, -.5f * ht - zoom_y * ht);
400

401 402
  float vignette_x = (p->center.x + 1.0) * 0.5 * wd;
  float vignette_y = (p->center.y + 1.0) * 0.5 * ht;
403 404 405

  cairo_translate(cr, vignette_x, vignette_y);

406 407 408 409
  float vignette_w = p->scale * 0.01 * 0.5 * wd; // start of falloff
  float vignette_h = p->scale * 0.01 * 0.5 * ht;
  float vignette_fx = vignette_w + p->falloff_scale * 0.01 * 0.5 * wd; // end of falloff
  float vignette_fy = vignette_h + p->falloff_scale * 0.01 * 0.5 * ht;
410 411 412

  if(p->autoratio == FALSE)
  {
413
    float factor1 = bigger_side / smaller_side;
414 415
    if(wd >= ht)
    {
416
      float factor2 = (2.0 - p->whratio) * factor1;
417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432

      if(p->whratio <= 1)
      {
        vignette_h *= factor1;
        vignette_w *= p->whratio;
        vignette_fx *= p->whratio;
        vignette_fy *= factor1;
      }
      else
      {
        vignette_h *= factor2;
        vignette_fy *= factor2;
      }
    }
    else
    {
433
      float factor2 = (p->whratio) * factor1;
434 435 436 437 438 439 440 441 442

      if(p->whratio <= 1)
      {
        vignette_w *= factor2;
        vignette_fx *= factor2;
      }
      else
      {
        vignette_w *= factor1;
443
        vignette_h *= (2.0 - p->whratio);
444
        vignette_fx *= factor1;
445
        vignette_fy *= (2.0 - p->whratio);
446 447 448 449
      }
    }
  }

450 451 452 453
  int grab = get_grab(pzx * wd - vignette_x, pzy * ht - vignette_y, vignette_w, -vignette_h, vignette_fx,
                      -vignette_fy, zoom_scale);
  cairo_set_line_cap(cr, CAIRO_LINE_CAP_ROUND);
  cairo_set_line_width(cr, DT_PIXEL_APPLY_DPI(3.0) / zoom_scale);
454 455
  cairo_set_source_rgba(cr, .3, .3, .3, .8);
  draw_overlay(cr, vignette_w, vignette_h, vignette_fx, vignette_fy, grab, zoom_scale);
456
  cairo_set_line_width(cr, DT_PIXEL_APPLY_DPI(1.0) / zoom_scale);
457 458 459 460
  cairo_set_source_rgba(cr, .8, .8, .8, .8);
  draw_overlay(cr, vignette_w, vignette_h, vignette_fx, vignette_fy, grab, zoom_scale);
}

461 462
// FIXME: Pumping of the opposite direction when changing width/height. See two FIXMEs further down.
int mouse_moved(struct dt_iop_module_t *self, double x, double y, double pressure, int which)
463 464
{
  dt_iop_vignette_gui_data_t *g = (dt_iop_vignette_gui_data_t *)self->gui_data;
465
  dt_iop_vignette_params_t *p = (dt_iop_vignette_params_t *)self->params;
466 467 468
  float wd = self->dev->preview_pipe->backbuf_width;
  float ht = self->dev->preview_pipe->backbuf_height;
  float bigger_side, smaller_side;
Henrik Andersson's avatar
Henrik Andersson committed
469 470 471 472 473 474 475 476 477 478
  if(wd >= ht)
  {
    bigger_side = wd;
    smaller_side = ht;
  }
  else
  {
    bigger_side = ht;
    smaller_side = wd;
  }
479 480
  dt_dev_zoom_t zoom = dt_control_get_dev_zoom();
  int closeup = dt_control_get_dev_closeup();
481 482 483 484 485 486 487 488
  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;
  static int old_grab = -1;
  int grab = old_grab;

489 490
  float vignette_x = (p->center.x + 1.0) * 0.5 * wd;
  float vignette_y = (p->center.y + 1.0) * 0.5 * ht;
491

492 493 494 495
  float vignette_w = p->scale * 0.01 * 0.5 * wd; // start of falloff
  float vignette_h = p->scale * 0.01 * 0.5 * ht;
  float vignette_fx = vignette_w + p->falloff_scale * 0.01 * 0.5 * wd; // end of falloff
  float vignette_fy = vignette_h + p->falloff_scale * 0.01 * 0.5 * ht;
496 497 498

  if(p->autoratio == FALSE)
  {
499
    float factor1 = bigger_side / smaller_side;
500 501
    if(wd >= ht)
    {
502
      float factor2 = (2.0 - p->whratio) * factor1;
503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518

      if(p->whratio <= 1)
      {
        vignette_h *= factor1;
        vignette_w *= p->whratio;
        vignette_fx *= p->whratio;
        vignette_fy *= factor1;
      }
      else
      {
        vignette_h *= factor2;
        vignette_fy *= factor2;
      }
    }
    else
    {
519
      float factor2 = (p->whratio) * factor1;
520 521 522 523 524 525 526 527 528

      if(p->whratio <= 1)
      {
        vignette_w *= factor2;
        vignette_fx *= factor2;
      }
      else
      {
        vignette_w *= factor1;
529
        vignette_h *= (2.0 - p->whratio);
530
        vignette_fx *= factor1;
531
        vignette_fy *= (2.0 - p->whratio);
532 533 534 535
      }
    }
  }

Henrik Andersson's avatar
Henrik Andersson committed
536 537
  if(grab == 0 || !(darktable.control->button_down && darktable.control->button_down_which == 1))
  {
538 539
    grab = get_grab(pzx * wd - vignette_x, pzy * ht - vignette_y, vignette_w, -vignette_h, vignette_fx,
                    -vignette_fy, zoom_scale);
540 541 542 543 544 545 546 547 548 549 550
  }

  if(darktable.control->button_down && darktable.control->button_down_which == 1)
  {
    if(grab == 0) // pan the image
    {
      dt_control_change_cursor(GDK_HAND1);
      return 0;
    }
    else if(grab == 1) // move the center
    {
551 552
      dt_bauhaus_slider_set(g->center_x, pzx * 2.0 - 1.0);
      dt_bauhaus_slider_set(g->center_y, pzy * 2.0 - 1.0);
553
    }
554
    else if(grab == 2) // change the width
555
    {
556 557 558
      float max = 0.5 * ((p->whratio <= 1.0) ? bigger_side * p->whratio : bigger_side);
      float new_vignette_w = MIN(bigger_side * 0.5, MAX(0.1, pzx * wd - vignette_x));
      float ratio = new_vignette_w / vignette_h;
559
      float new_scale = 100.0 * new_vignette_w / max;
560 561
      // FIXME: When going over the 1.0 boundary from wide to narrow (>1.0 -> <=1.0) the height slightly
      // changes, depending on speed.
562 563 564 565 566
      //        I guess we have to split the computation.
      if(ratio <= 1.0)
      {
        if(which == GDK_CONTROL_MASK)
        {
567
          dt_bauhaus_slider_set(g->scale, new_scale);
568 569 570
        }
        else
        {
571
          dt_bauhaus_slider_set(g->whratio, ratio);
572 573 574 575
        }
      }
      else
      {
576
        dt_bauhaus_slider_set(g->scale, new_scale);
577 578 579 580

        if(which != GDK_CONTROL_MASK)
        {
          float new_whratio = 2.0 - 1.0 / ratio;
581
          dt_bauhaus_slider_set(g->whratio, new_whratio);
582 583 584
        }
      }
    }
585
    else if(grab == 4) // change the height
586
    {
587 588 589 590 591
      float new_vignette_h = MIN(bigger_side * 0.5, MAX(0.1, vignette_y - pzy * ht));
      float ratio = new_vignette_h / vignette_w;
      float max = 0.5 * ((ratio <= 1.0) ? bigger_side * (2.0 - p->whratio) : bigger_side);
      // FIXME: When going over the 1.0 boundary from narrow to wide (>1.0 -> <=1.0) the width slightly
      // changes, depending on speed.
592 593 594 595 596
      //        I guess we have to split the computation.
      if(ratio <= 1.0)
      {
        if(which == GDK_CONTROL_MASK)
        {
597
          float new_scale = 100.0 * new_vignette_h / max;
598
          dt_bauhaus_slider_set(g->scale, new_scale);
599 600 601
        }
        else
        {
602
          dt_bauhaus_slider_set(g->whratio, 2.0 - ratio);
603 604 605 606
        }
      }
      else
      {
607
        float new_scale = 100.0 * new_vignette_h / max;
608
        dt_bauhaus_slider_set(g->scale, new_scale);
609 610 611 612

        if(which != GDK_CONTROL_MASK)
        {
          float new_whratio = 1.0 / ratio;
613
          dt_bauhaus_slider_set(g->whratio, new_whratio);
614 615 616
        }
      }
    }
617
    else if(grab == 8) // change the falloff on the right
618
    {
619 620
      float new_vignette_fx = pzx * wd - vignette_x;
      float max = 0.5 * ((p->whratio <= 1.0) ? bigger_side * p->whratio : bigger_side);
621 622
      float delta_x = MIN(max, MAX(0.0, new_vignette_fx - vignette_w));
      float new_falloff = 100.0 * delta_x / max;
623
      dt_bauhaus_slider_set(g->falloff_scale, new_falloff);
624 625 626
    }
    else if(grab == 16) // change the falloff on the top
    {
627 628
      float new_vignette_fy = vignette_y - pzy * ht;
      float max = 0.5 * ((p->whratio > 1.0) ? bigger_side * (2.0 - p->whratio) : bigger_side);
629 630
      float delta_y = MIN(max, MAX(0.0, new_vignette_fy - vignette_h));
      float new_falloff = 100.0 * delta_y / max;
631
      dt_bauhaus_slider_set(g->falloff_scale, new_falloff);
632
    }
633
    dt_control_queue_redraw_center();
634 635 636 637
    return 1;
  }
  else if(grab)
  {
638 639 640 641 642 643 644 645 646 647
    if(grab == 1)
      dt_control_change_cursor(GDK_FLEUR);
    else if(grab == 2)
      dt_control_change_cursor(GDK_SB_H_DOUBLE_ARROW);
    else if(grab == 4)
      dt_control_change_cursor(GDK_SB_V_DOUBLE_ARROW);
    else if(grab == 8)
      dt_control_change_cursor(GDK_SB_H_DOUBLE_ARROW);
    else if(grab == 16)
      dt_control_change_cursor(GDK_SB_V_DOUBLE_ARROW);
648 649 650 651 652 653
  }
  else
  {
    if(old_grab != grab) dt_control_change_cursor(GDK_LEFT_PTR);
  }
  old_grab = grab;
654
  dt_control_queue_redraw_center();
655 656 657
  return 0;
}

658 659
int button_pressed(struct dt_iop_module_t *self, double x, double y, double pressure, int which, int type,
                   uint32_t state)
660
{
661
  if(which == 1) return 1;
662 663 664 665 666
  return 0;
}

int button_released(struct dt_iop_module_t *self, double x, double y, int which, uint32_t state)
{
667
  if(which == 1) return 1;
668
  return 0;
669
}
670

671 672
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)
673
{
674
  const dt_iop_vignette_data_t *data = (dt_iop_vignette_data_t *)piece->data;
675
  const dt_iop_roi_t *buf_in = &piece->buf_in;
676
  const int ch = piece->colors;
677
  const gboolean unbound = data->unbound;
678

johannes hanika's avatar
johannes hanika committed
679
  /* Center coordinates of buf_in, these should not consider buf_in->{x,y}! */
680
  const dt_iop_vector_2d_t buf_center = { buf_in->width * .5f, buf_in->height * .5f };
681
  /* Center coordinates of vignette center */
682 683
  const dt_iop_vector_2d_t vignette_center = { buf_center.x + data->center.x * buf_in->width / 2.0,
                                               buf_center.y + data->center.y * buf_in->height / 2.0 };
684
  /* Coordinates of vignette_center in terms of roi_in */
685 686
  const dt_iop_vector_2d_t roi_center
      = { vignette_center.x * roi_in->scale - roi_in->x, vignette_center.y * roi_in->scale - roi_in->y };
687 688 689 690
  float xscale;
  float yscale;

  /* w/h ratio follows piece dimensions */
691
  if(data->autoratio)
692
  {
693 694
    xscale = 2.0 / (buf_in->width * roi_out->scale);
    yscale = 2.0 / (buf_in->height * roi_out->scale);
695
  }
696
  else /* specified w/h ratio, scale proportional to longest side */
697 698 699
  {
    const float basis = 2.0 / (MAX(buf_in->height, buf_in->width) * roi_out->scale);
    // w/h ratio from 0-1 use as-is
700
    if(data->whratio <= 1.0)
701
    {
702 703
      yscale = basis;
      xscale = yscale / data->whratio;
704 705 706 707 708
    }
    // w/h ratio from 1-2 interpret as 1-inf
    // that is, the h/w ratio + 1
    else
    {
709 710
      xscale = basis;
      yscale = xscale / (2.0 - data->whratio);
711 712
    }
  }
713
  const float dscale = data->scale / 100.0;
714
  // A minimum falloff is used, based on the image size, to smooth out aliasing artifacts
715 716 717 718 719
  const float min_falloff = 100.0 / MIN(buf_in->width, buf_in->height);
  const float fscale = MAX(data->falloff_scale, min_falloff) / 100.0;
  const float shape = MAX(data->shape, 0.001);
  const float exp1 = 2.0 / shape;
  const float exp2 = shape / 2.0;
720
  // Pre-scale the center offset
721
  const dt_iop_vector_2d_t roi_center_scaled = { roi_center.x * xscale, roi_center.y * yscale };
722

723 724 725 726 727
  float dither = 0.0f;

  switch(data->dithering)
  {
    case DITHER_8BIT:
728
      dither = 1.0f / 256;
729 730
      break;
    case DITHER_16BIT:
731
      dither = 1.0f / 65536;
732 733 734 735 736 737
      break;
    case DITHER_OFF:
    default:
      dither = 0.0f;
  }

738
  unsigned int *const tea_states = calloc(2 * dt_get_num_threads(), sizeof(unsigned int));
739

740
#ifdef _OPENMP
741
#pragma omp parallel for default(none) shared(data, yscale, xscale, dither) schedule(static)
742
#endif
743
  for(int j = 0; j < roi_out->height; j++)
744
  {
745
    const size_t k = (size_t)ch * roi_out->width * j;
746 747
    const float *in = (const float *)ivoid + k;
    float *out = (float *)ovoid + k;
748 749
    unsigned int *tea_state = tea_states + 2 * dt_get_thread_num();
    tea_state[0] = j * roi_out->height + dt_get_thread_num();
750
    for(int i = 0; i < roi_out->width; i++, in += ch, out += ch)
751
    {
752
      // current pixel coord translated to local coord
753 754
      const dt_iop_vector_2d_t pv
          = { fabsf(i * xscale - roi_center_scaled.x), fabsf(j * yscale - roi_center_scaled.y) };
755 756

      // Calculate the pixel weight in vignette
757 758 759
      const float cplen = powf(powf(pv.x, exp1) + powf(pv.y, exp1), exp2); // Length from center to pv
      float weight = 0.0;
      float dith = 0.0;
760

761
      if(cplen >= dscale) // pixel is outside the inner vignette circle, lets calculate weight of vignette
762
      {
763 764
        weight = ((cplen - dscale) / fscale);
        if(weight >= 1.0)
765
          weight = 1.0;
766
        else if(weight <= 0.0)
767 768
          weight = 0.0;
        else
769
        {
770
          weight = 0.5 - cosf(M_PI * weight) / 2.0;
771 772 773
          encrypt_tea(tea_state);
          dith = dither * tpdf(tea_state[0]);
        }
774 775 776
      }

      // Let's apply weighted effect on brightness and desaturation
777 778
      float col0 = in[0], col1 = in[1], col2 = in[2], col3 = in[3];
      if(weight > 0)
779
      {
780
        // Then apply falloff vignette
781 782 783 784 785
        float falloff = (data->brightness < 0) ? (1.0 + (weight * data->brightness))
                                               : (weight * data->brightness);
        col0 = data->brightness < 0 ? col0 * falloff + dith : col0 + falloff + dith;
        col1 = data->brightness < 0 ? col1 * falloff + dith : col1 + falloff + dith;
        col2 = data->brightness < 0 ? col2 * falloff + dith : col2 + falloff + dith;
786

787 788 789
        col0 = unbound ? col0 : CLIP(col0);
        col1 = unbound ? col1 : CLIP(col1);
        col2 = unbound ? col2 : CLIP(col2);
790 791

        // apply saturation
792 793 794 795 796 797 798 799 800
        float mv = (col0 + col1 + col2) / 3.0;
        float wss = weight * data->saturation;
        col0 = col0 - ((mv - col0) * wss);
        col1 = col1 - ((mv - col1) * wss);
        col2 = col2 - ((mv - col2) * wss);

        col0 = unbound ? col0 : CLIP(col0);
        col1 = unbound ? col1 : CLIP(col1);
        col2 = unbound ? col2 : CLIP(col2);
801
      }
802

803 804 805 806
      out[0] = col0;
      out[1] = col1;
      out[2] = col2;
      out[3] = col3;
807
    }
808
  }
809 810

  free(tea_states);
811 812
}

813 814

#ifdef HAVE_OPENCL
815
int process_cl(struct dt_iop_module_t *self, dt_dev_pixelpipe_iop_t *piece, cl_mem dev_in, cl_mem dev_out,
816
               const dt_iop_roi_t *const roi_in, const dt_iop_roi_t *const roi_out)
817 818 819 820 821 822 823 824 825 826 827 828
{
  dt_iop_vignette_data_t *data = (dt_iop_vignette_data_t *)piece->data;
  dt_iop_vignette_global_data_t *gd = (dt_iop_vignette_global_data_t *)self->data;

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

  const dt_iop_roi_t *buf_in = &piece->buf_in;

  /* Center coordinates of buf_in, these should not consider buf_in->{x,y}! */
829
  const dt_iop_vector_2d_t buf_center = { buf_in->width * .5f, buf_in->height * .5f };
830
  /* Center coordinates of vignette center */
831 832
  const dt_iop_vector_2d_t vignette_center = { buf_center.x + data->center.x * buf_in->width / 2.0,
                                               buf_center.y + data->center.y * buf_in->height / 2.0 };
833
  /* Coordinates of vignette_center in terms of roi_in */
834 835
  const dt_iop_vector_2d_t roi_center
      = { vignette_center.x * roi_in->scale - roi_in->x, vignette_center.y * roi_in->scale - roi_in->y };
836 837 838 839
  float xscale;
  float yscale;

  /* w/h ratio follows piece dimensions */
840
  if(data->autoratio)
841
  {
842 843
    xscale = 2.0 / (buf_in->width * roi_out->scale);
    yscale = 2.0 / (buf_in->height * roi_out->scale);
844
  }
845
  else /* specified w/h ratio, scale proportional to longest side */
846 847 848
  {
    const float basis = 2.0 / (MAX(buf_in->height, buf_in->width) * roi_out->scale);
    // w/h ratio from 0-1 use as-is
849
    if(data->whratio <= 1.0)
850
    {
851 852
      yscale = basis;
      xscale = yscale / data->whratio;
853 854 855 856 857
    }
    // w/h ratio from 1-2 interpret as 1-inf
    // that is, the h/w ratio + 1
    else
    {
858 859
      xscale = basis;
      yscale = xscale / (2.0 - data->whratio);
860 861
    }
  }
862
  const float dscale = data->scale / 100.0;
863
  // A minimum falloff is used, based on the image size, to smooth out aliasing artifacts
864 865 866 867 868
  const float min_falloff = 100.0 / MIN(buf_in->width, buf_in->height);
  const float fscale = MAX(data->falloff_scale, min_falloff) / 100.0;
  const float shape = MAX(data->shape, 0.001);
  const float exp1 = 2.0 / shape;
  const float exp2 = shape / 2.0;
869
  // Pre-scale the center offset
870
  const dt_iop_vector_2d_t roi_center_scaled = { roi_center.x * xscale, roi_center.y * yscale };
871

872 873 874 875 876
  float dither = 0.0f;

  switch(data->dithering)
  {
    case DITHER_8BIT:
877
      dither = 1.0f / 256;
878 879
      break;
    case DITHER_16BIT:
880
      dither = 1.0f / 65536;
881 882 883 884 885 886
      break;
    case DITHER_OFF:
    default:
      dither = 0.0f;
  }

887 888 889 890 891
  float scale[2] = { xscale, yscale };
  float roi_center_scaled_f[2] = { roi_center_scaled.x, roi_center_scaled.y };
  float expt[2] = { exp1, exp2 };
  float brightness = data->brightness;
  float saturation = data->saturation;
892
  int unbound = data->unbound;
893 894 895 896 897 898 899

  size_t sizes[2] = { ROUNDUPWD(width), ROUNDUPHT(height) };

  dt_opencl_set_kernel_arg(devid, gd->kernel_vignette, 0, sizeof(cl_mem), &dev_in);
  dt_opencl_set_kernel_arg(devid, gd->kernel_vignette, 1, sizeof(cl_mem), &dev_out);
  dt_opencl_set_kernel_arg(devid, gd->kernel_vignette, 2, sizeof(int), &width);
  dt_opencl_set_kernel_arg(devid, gd->kernel_vignette, 3, sizeof(int), &height);
900 901 902
  dt_opencl_set_kernel_arg(devid, gd->kernel_vignette, 4, 2 * sizeof(float), &scale);
  dt_opencl_set_kernel_arg(devid, gd->kernel_vignette, 5, 2 * sizeof(float), &roi_center_scaled_f);
  dt_opencl_set_kernel_arg(devid, gd->kernel_vignette, 6, 2 * sizeof(float), &expt);
903 904 905 906
  dt_opencl_set_kernel_arg(devid, gd->kernel_vignette, 7, sizeof(float), &dscale);
  dt_opencl_set_kernel_arg(devid, gd->kernel_vignette, 8, sizeof(float), &fscale);
  dt_opencl_set_kernel_arg(devid, gd->kernel_vignette, 9, sizeof(float), &brightness);
  dt_opencl_set_kernel_arg(devid, gd->kernel_vignette, 10, sizeof(float), &saturation);
907
  dt_opencl_set_kernel_arg(devid, gd->kernel_vignette, 11, sizeof(float), &dither);
908
  dt_opencl_set_kernel_arg(devid, gd->kernel_vignette, 12, sizeof(int), &unbound);
909 910 911 912 913 914 915 916 917 918 919 920 921 922 923
  err = dt_opencl_enqueue_kernel_2d(devid, gd->kernel_vignette, sizes);
  if(err != CL_SUCCESS) goto error;

  return TRUE;

error:
  dt_print(DT_DEBUG_OPENCL, "[opencl_vignette] 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
924 925
  dt_iop_vignette_global_data_t *gd
      = (dt_iop_vignette_global_data_t *)malloc(sizeof(dt_iop_vignette_global_data_t));
926 927 928 929 930 931 932 933 934 935 936 937 938 939
  module->data = gd;
  gd->kernel_vignette = dt_opencl_create_kernel(program, "vignette");
}


void cleanup_global(dt_iop_module_so_t *module)
{
  dt_iop_vignette_global_data_t *gd = (dt_iop_vignette_global_data_t *)module->data;
  dt_opencl_free_kernel(gd->kernel_vignette);
  free(module->data);
  module->data = NULL;
}


940
static void scale_callback(GtkWidget *slider, gpointer user_data)
941 942 943 944
{
  dt_iop_module_t *self = (dt_iop_module_t *)user_data;
  if(self->dt->gui->reset) return;
  dt_iop_vignette_params_t *p = (dt_iop_vignette_params_t *)self->params;
945
  p->scale = dt_bauhaus_slider_get(slider);
946
  dt_dev_add_history_item(darktable.develop, self, TRUE);
947 948
}

949
static void falloff_scale_callback(GtkWidget *slider, gpointer user_data)
950 951 952 953
{
  dt_iop_module_t *self = (dt_iop_module_t *)user_data;
  if(self->dt->gui->reset) return;
  dt_iop_vignette_params_t *p = (dt_iop_vignette_params_t *)self->params;
954
  p->falloff_scale = dt_bauhaus_slider_get(slider);
955
  dt_dev_add_history_item(darktable.develop, self, TRUE);
956 957
}

958
static void brightness_callback(GtkWidget *slider, gpointer user_data)
959 960 961 962
{
  dt_iop_module_t *self = (dt_iop_module_t *)user_data;
  if(self->dt->gui->reset) return;
  dt_iop_vignette_params_t *p = (dt_iop_vignette_params_t *)self->params;
963
  p->brightness = dt_bauhaus_slider_get(slider);
964
  dt_dev_add_history_item(darktable.develop, self, TRUE);
965 966
}

967
static void saturation_callback(GtkWidget *slider, gpointer user_data)
968 969 970 971
{
  dt_iop_module_t *self = (dt_iop_module_t *)user_data;
  if(self->dt->gui->reset) return;
  dt_iop_vignette_params_t *p = (dt_iop_vignette_params_t *)self->params;
972
  p->saturation = dt_bauhaus_slider_get(slider);
973
  dt_dev_add_history_item(darktable.develop, self, TRUE);
974 975
}

976
static void centerx_callback(GtkWidget *slider, gpointer user_data)
977 978 979 980
{
  dt_iop_module_t *self = (dt_iop_module_t *)user_data;
  if(self->dt->gui->reset) return;
  dt_iop_vignette_params_t *p = (dt_iop_vignette_params_t *)self->params;
981
  p->center.x = dt_bauhaus_slider_get(slider);
982 983 984
  dt_dev_add_history_item(darktable.develop, self, TRUE);
}

985
static void centery_callback(GtkWidget *slider, gpointer user_data)
986 987 988 989
{
  dt_iop_module_t *self = (dt_iop_module_t *)user_data;
  if(self->dt->gui->reset) return;
  dt_iop_vignette_params_t *p = (dt_iop_vignette_params_t *)self->params;
990
  p->center.y = dt_bauhaus_slider_get(slider);
991 992 993
  dt_dev_add_history_item(darktable.develop, self, TRUE);
}

994
static void shape_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_vignette_params_t *p = (dt_iop_vignette_params_t *)self->params;
999
  p->shape = dt_bauhaus_slider_get(slider);
1000 1001 1002
  dt_dev_add_history_item(darktable.develop, self, TRUE);
}

1003
static void autoratio_callback(GtkToggleButton *button, 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_vignette_params_t *p = (dt_iop_vignette_params_t *)self->params;
1008 1009 1010
  p->autoratio = gtk_toggle_button_get_active(button);
  dt_iop_vignette_gui_data_t *g = (dt_iop_vignette_gui_data_t *)self->gui_data;
  gtk_widget_set_sensitive(GTK_WIDGET(g->whratio), !p->autoratio);
1011
  dt_dev_add_history_item(darktable.develop, self, TRUE);
1012
}
1013

1014
static void whratio_callback(GtkWidget *slider, gpointer user_data)
1015 1016 1017 1018
{
  dt_iop_module_t *self = (dt_iop_module_t *)user_data;
  if(self->dt->gui->reset) return;
  dt_iop_vignette_params_t *p = (dt_iop_vignette_params_t *)self->params;
1019
  p->whratio = dt_bauhaus_slider_get(slider);
1020
  dt_dev_add_history_item(darktable.develop, self, TRUE);
1021 1022
}

1023
static void dithering_callback(GtkWidget *widget, gpointer user_data)
1024 1025 1026 1027 1028 1029 1030 1031
{
  dt_iop_module_t *self = (dt_iop_module_t *)user_data;
  if(self->dt->gui->reset) return;
  dt_iop_vignette_params_t *p = (dt_iop_vignette_params_t *)self->params;
  p->dithering = (dt_iop_dither_t)dt_bauhaus_combobox_get(widget);
  dt_dev_add_history_item(darktable.develop, self, TRUE);
}

1032 1033
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)
1034 1035 1036 1037
{
  dt_iop_vignette_params_t *p = (dt_iop_vignette_params_t *)p1;
  dt_iop_vignette_data_t *d = (dt_iop_vignette_data_t *)piece->data;
  d->scale = p->scale;
1038
  d->falloff_scale = p->falloff_scale;
1039 1040 1041 1042 1043 1044 1045
  d->brightness = p->brightness;
  d->saturation = p->saturation;
  d->center = p->center;
  d->autoratio = p->autoratio;
  d->whratio = p->whratio;
  d->shape = p->shape;
  d->dithering = p->dithering;
1046
  d->unbound = p->unbound;
1047 1048
}

1049
void init_presets(dt_iop_module_so_t *self)
1050
{
1051
  DT_DEBUG_SQLITE3_EXEC(dt_database_get(darktable.db), "BEGIN", NULL, NULL, NULL);
1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062
  dt_iop_vignette_params_t p;
  p.scale = 40.0f;
  p.falloff_scale = 100.0f;
  p.brightness = -1.0f;
  p.saturation = 0.5f;
  p.center.x = 0.0f;
  p.center.y = 0.0f;
  p.autoratio = FALSE;
  p.whratio = 1.0f;
  p.shape = 1.0f;
  p.dithering = 0;
1063
  p.unbound = TRUE;
1064
  dt_gui_presets_add_generic(_("lomo"), self->op, self->version(), &p, sizeof(p), 1);
1065
  DT_DEBUG_SQLITE3_EXEC(dt_database_get(darktable.db), "COMMIT", NULL, NULL, NULL);
1066 1067
}

1068
void init_pipe(struct dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
1069 1070 1071 1072 1073
{
  piece->data = malloc(sizeof(dt_iop_vignette_data_t));
  self->commit_params(self, self->default_params, pipe, piece);
}

1074
void cleanup_pipe(struct dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
1075 1076
{
  free(piece->data);
1077
  piece->data = NULL;
1078 1079 1080 1081 1082 1083 1084
}

void gui_update(struct dt_iop_module_t *self)
{
  dt_iop_module_t *module = (dt_iop_module_t *)self;
  dt_iop_vignette_gui_data_t *g = (dt_iop_vignette_gui_data_t *)self->gui_data;
  dt_iop_vignette_params_t *p = (dt_iop_vignette_params_t *)module->params;
1085 1086 1087 1088 1089 1090
  dt_bauhaus_slider_set(g->scale, p->scale);
  dt_bauhaus_slider_set(g->falloff_scale, p->falloff_scale);
  dt_bauhaus_slider_set(g->brightness, p->brightness);
  dt_bauhaus_slider_set(g->saturation, p->saturation);
  dt_bauhaus_slider_set(g->center_x, p->center.x);
  dt_bauhaus_slider_set(g->center_y, p->center.y);
1091
  gtk_toggle_button_set_active(g->autoratio, p->autoratio);
1092 1093
  dt_bauhaus_slider_set(g->whratio, p->whratio);
  dt_bauhaus_slider_set(g->shape, p->shape);
1094
  gtk_widget_set_sensitive(GTK_WIDGET(g->whratio), !p->autoratio);
1095
  dt_bauhaus_combobox_set(g->dithering, p->dithering);
1096 1097 1098 1099
}

void init(dt_iop_module_t *module)
{
1100 1101
  module->params = calloc(1, sizeof(dt_iop_vignette_params_t));
  module->default_params = calloc(1, sizeof(dt_iop_vignette_params_t));
1102
  module->default_enabled = 0;
Heiko Bauke's avatar
Heiko Bauke committed
1103
  module->priority = 852; // module order created by iop_dependencies.py, do not edit!
1104 1105
  module->params_size = sizeof(dt_iop_vignette_params_t);
  module->gui_data = NULL;
1106 1107
  dt_iop_vignette_params_t tmp
      = (dt_iop_vignette_params_t){ 80, 50, -0.5, -0.5, { 0, 0 }, FALSE, 1.0, 1.0, DITHER_OFF, TRUE };
1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122
  memcpy(module->params, &tmp, sizeof(dt_iop_vignette_params_t));
  memcpy(module->default_params, &tmp, sizeof(dt_iop_vignette_params_t));
}

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

void gui_init(struct dt_iop_module_t *self)
{
  self->gui_data = malloc(sizeof(dt_iop_vignette_gui_data_t));
  dt_iop_vignette_gui_data_t *g = (dt_iop_vignette_gui_data_t *)self->gui_data;
  dt_iop_vignette_params_t *p = (dt_iop_vignette_params_t *)self->params;
1123
  GtkWidget *hbox, *label1;
Olivier's avatar
Olivier committed
1124

1125
  self->widget = gtk_box_new(GTK_ORIENTATION_VERTICAL, DT_BAUHAUS_SPACE);
Olivier's avatar
Olivier committed
1126

1127
  label1 = dtgtk_reset_label_new(_("automatic ratio"), self, &p->autoratio, sizeof p->autoratio);
1128

1129 1130 1131 1132 1133 1134 1135 1136
  g->scale = dt_bauhaus_slider_new_with_range(self, 0.0, 100.0, 0.5, p->scale, 2);
  g->falloff_scale = dt_bauhaus_slider_new_with_range(self, 0.0, 100.0, 1.0, p->falloff_scale, 2);
  g->brightness = dt_bauhaus_slider_new_with_range(self, -1.0, 1.0, 0.01, p->brightness, 3);
  g->saturation = dt_bauhaus_slider_new_with_range(self, -1.0, 1.0, 0.01, p->saturation, 3);
  g->center_x = dt_bauhaus_slider_new_with_range(self, -1.0, 1.0, 0.01, p->center.x, 3);
  g->center_y = dt_bauhaus_slider_new_with_range(self, -1.0, 1.0, 0.01, p->center.y, 3);
  g->shape = dt_bauhaus_slider_new_with_range(self, 0.0, 5.0, 0.1, p->shape, 2);
  g->whratio = dt_bauhaus_slider_new_with_range(self, 0.0, 2.0, 0.01, p->shape, 3);
1137
  g->autoratio = GTK_TOGGLE_BUTTON(gtk_toggle_button_new_with_label(_("automatic")));
1138
  g->dithering = dt_bauhaus_combobox_new(self);
1139 1140 1141 1142

  dt_bauhaus_combobox_add(g->dithering, _("off"));
  dt_bauhaus_combobox_add(g->dithering, _("8-bit output"));
  dt_bauhaus_combobox_add(g->dithering, _("16-bit output"));
1143

1144 1145
  dt_bauhaus_slider_set_format(g->scale, "%.02f%%");
  dt_bauhaus_slider_set_format(g->falloff_scale, "%.02f%%");
1146 1147 1148 1149 1150 1151 1152 1153 1154
  dt_bauhaus_widget_set_label(g->scale, NULL, _("scale"));
  dt_bauhaus_widget_set_label(g->falloff_scale, NULL, _("fall-off strength"));
  dt_bauhaus_widget_set_label(g->brightness, NULL, _("brightness"));
  dt_bauhaus_widget_set_label(g->saturation, NULL, _("saturation"));
  dt_bauhaus_widget_set_label(g->center_x, NULL, _("horizontal center"));
  dt_bauhaus_widget_set_label(g->center_y, NULL, _("vertical center"));
  dt_bauhaus_widget_set_label(g->shape, NULL, _("shape"));
  dt_bauhaus_widget_set_label(g->whratio, NULL, _("width/height ratio"));
  dt_bauhaus_widget_set_label(g->dithering, NULL, _("dithering"));
Olivier's avatar
Olivier committed
1155

1156 1157
  gtk_widget_set_sensitive(GTK_WIDGET(g->whratio), !p->autoratio);

1158
  hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
Olivier's avatar
Olivier committed
1159 1160 1161
  gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(label1), TRUE, TRUE, 0);
  gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(g->autoratio), TRUE, TRUE, 0);

1162 1163 1164 1165 1166 1167 1168 1169 1170
  gtk_box_pack_start(GTK_BOX(self->widget), g->scale, TRUE, TRUE, 0);
  gtk_box_pack_start(GTK_BOX(self->widget), g->falloff_scale, TRUE, TRUE, 0);
  gtk_box_pack_start(GTK_BOX(self->widget), g->brightness, TRUE, TRUE, 0);
  gtk_box_pack_start(GTK_BOX(self->widget), g->saturation, TRUE, TRUE, 0);
  gtk_box_pack_start(GTK_BOX(self->widget), g->center_x, TRUE, TRUE, 0);
  gtk_box_pack_start(GTK_BOX(self->widget), g->center_y, TRUE, TRUE, 0);
  gtk_box_pack_start(GTK_BOX(self->