bilat.c 21.2 KB
Newer Older
1
/*
2
    This file is part of darktable,
3
    copyright (c) 2009--2016 johannes hanika.
4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22

    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
#include "config.h"
#endif
// our includes go first:
#include "bauhaus/bauhaus.h"
23 24
#include "common/bilateral.h"
#include "common/bilateralcl.h"
25
#include "common/locallaplacian.h"
26
#include "common/locallaplaciancl.h"
27
#include "develop/imageop.h"
28
#include "develop/imageop_math.h"
29
#include "develop/tiling.h"
30
#include "gui/gtk.h"
31
#include "iop/iop_api.h"
32 33 34 35 36 37

#include <gtk/gtk.h>
#include <stdlib.h>

// this is the version of the modules parameters,
// and includes version information about compile-time dt
38
DT_MODULE_INTROSPECTION(3, dt_iop_bilat_params_t)
39 40 41 42 43 44 45

typedef enum dt_iop_bilat_mode_t
{
  s_mode_bilateral = 0,
  s_mode_local_laplacian = 1,
}
dt_iop_bilat_mode_t;
46

47 48 49 50 51 52
typedef struct dt_iop_bilat_params_t
{
  uint32_t mode;
  float sigma_r;
  float sigma_s;
  float detail;
53
  float midtone;
54 55 56
}
dt_iop_bilat_params_t;

57
typedef struct dt_iop_bilat_params_v2_t
58
{
59
  uint32_t mode;
60 61 62
  float sigma_r;
  float sigma_s;
  float detail;
63
}
64
dt_iop_bilat_params_v2_t;
65

66
typedef struct dt_iop_bilat_params_v1_t
67 68 69 70
{
  float sigma_r;
  float sigma_s;
  float detail;
71 72 73 74
}
dt_iop_bilat_params_v1_t;

typedef dt_iop_bilat_params_t dt_iop_bilat_data_t;
75

76
typedef struct dt_iop_bilat_gui_data_t
77
{
78 79
  GtkWidget *highlights;
  GtkWidget *shadows;
80
  GtkWidget *midtone;
81 82
  GtkWidget *spatial;
  GtkWidget *range;
83
  GtkWidget *detail;
84
  GtkWidget *mode;
85 86 87 88

  local_laplacian_boundary_t ll_boundary;
  uint64_t hash;
  dt_pthread_mutex_t lock;
89 90
}
dt_iop_bilat_gui_data_t;
91 92 93 94

// this returns a translatable name
const char *name()
{
95
  return _("local contrast");
96 97 98
}

// some additional flags (self explanatory i think):
99
int flags()
100
{
101
  return IOP_FLAGS_INCLUDE_IN_STYLES | IOP_FLAGS_SUPPORTS_BLENDING | IOP_FLAGS_ALLOW_TILING;
102 103 104
}

// where does it appear in the gui?
105
int groups()
106
{
107
  return IOP_GROUP_TONE;
108 109
}

110 111 112 113
int legacy_params(
    dt_iop_module_t *self, const void *const old_params, const int old_version,
    void *new_params, const int new_version)
{
114 115 116 117 118 119 120 121 122 123 124 125
  if(old_version == 2 && new_version == 3)
  {
    const dt_iop_bilat_params_v2_t *p2 = old_params;
    dt_iop_bilat_params_t *p = new_params;
    p->detail  = p2->detail;
    p->sigma_r = p2->sigma_r;
    p->sigma_s = p2->sigma_s;
    p->midtone = 0.2f;
    p->mode    = p2->mode;
    return 0;
  }
  else if(old_version == 1 && new_version == 3)
126
  {
127 128 129 130 131 132 133
    const dt_iop_bilat_params_v1_t *p1 = old_params;
    dt_iop_bilat_params_t *p = new_params;
    p->detail  = p1->detail;
    p->sigma_r = p1->sigma_r;
    p->sigma_s = p1->sigma_s;
    p->midtone = 0.2f;
    p->mode    = s_mode_bilateral;
134 135 136 137 138
    return 0;
  }
  return 1;
}

139
#ifdef HAVE_OPENCL
140
int process_cl(struct dt_iop_module_t *self, dt_dev_pixelpipe_iop_t *piece, cl_mem dev_in, cl_mem dev_out,
141
               const dt_iop_roi_t *const roi_in, const dt_iop_roi_t *const roi_out)
142
{
143
  dt_iop_bilat_data_t *d = (dt_iop_bilat_data_t *)piece->data;
144

145 146 147 148 149 150 151 152 153 154
  if(d->mode == s_mode_bilateral)
  {
    // the total scale is composed of scale before input to the pipeline (iscale),
    // and the scale of the roi.
    const float scale = piece->iscale / roi_in->scale;
    const float sigma_r = d->sigma_r; // does not depend on scale
    const float sigma_s = d->sigma_s / scale;
    cl_int err = -666;

    dt_bilateral_cl_t *b
155
      = dt_bilateral_init_cl(piece->pipe->devid, roi_in->width, roi_in->height, sigma_s, sigma_r);
156 157 158 159 160 161 162 163 164
    if(!b) goto error;
    err = dt_bilateral_splat_cl(b, dev_in);
    if(err != CL_SUCCESS) goto error;
    err = dt_bilateral_blur_cl(b);
    if(err != CL_SUCCESS) goto error;
    err = dt_bilateral_slice_cl(b, dev_in, dev_out, d->detail);
    if(err != CL_SUCCESS) goto error;
    dt_bilateral_free_cl(b);
    return TRUE;
165
error:
166 167 168 169 170 171 172
    dt_bilateral_free_cl(b);
    dt_print(DT_DEBUG_OPENCL, "[opencl_bilateral] couldn't enqueue kernel! %d\n", err);
    return FALSE;
  }
  else // mode == s_mode_local_laplacian
  {
    dt_local_laplacian_cl_t *b = dt_local_laplacian_init_cl(piece->pipe->devid, roi_in->width, roi_in->height,
173
        d->midtone, d->sigma_s, d->sigma_r, d->detail);
174 175 176 177 178 179 180 181
    if(!b) goto error_ll;
    if(dt_local_laplacian_cl(b, dev_in, dev_out) != CL_SUCCESS) goto error_ll;
    dt_local_laplacian_free_cl(b);
    return TRUE;
error_ll:
    dt_local_laplacian_free_cl(b);
    return FALSE;
  }
182 183 184
}
#endif

185

186 187 188
void tiling_callback(struct dt_iop_module_t *self, struct dt_dev_pixelpipe_iop_t *piece,
                     const dt_iop_roi_t *roi_in, const dt_iop_roi_t *roi_out,
                     struct dt_develop_tiling_t *tiling)
189
{
190
  dt_iop_bilat_data_t *d = (dt_iop_bilat_data_t *)piece->data;
191 192
  // the total scale is composed of scale before input to the pipeline (iscale),
  // and the scale of the roi.
193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230

  if(d->mode == s_mode_bilateral)
  {
    const float scale = piece->iscale / roi_in->scale;
    const float sigma_r = d->sigma_r;
    const float sigma_s = d->sigma_s / scale;

    const int width = roi_in->width;
    const int height = roi_in->height;
    const int channels = piece->colors;

    const size_t basebuffer = width * height * channels * sizeof(float);

    tiling->factor = 2.0f + (float)dt_bilateral_memory_use(width, height, sigma_s, sigma_r) / basebuffer;
    tiling->maxbuf
        = fmax(1.0f, (float)dt_bilateral_singlebuffer_size(width, height, sigma_s, sigma_r) / basebuffer);
    tiling->overhead = 0;
    tiling->overlap = ceilf(4 * sigma_s);
    tiling->xalign = 1;
    tiling->yalign = 1;
  }
  else  // mode == s_mode_local_laplacian
  {
    const int width = roi_in->width;
    const int height = roi_in->height;
    const int channels = piece->colors;

    const size_t basebuffer = width * height * channels * sizeof(float);
    const int rad = MIN(roi_in->width, ceilf(256 * roi_in->scale / piece->iscale));

    tiling->factor = 2.0f + (float)local_laplacian_memory_use(width, height) / basebuffer;
    tiling->maxbuf
        = fmax(1.0f, (float)local_laplacian_singlebuffer_size(width, height) / basebuffer);
    tiling->overhead = 0;
    tiling->overlap = rad;
    tiling->xalign = 1;
    tiling->yalign = 1;
  }
231 232
}

233 234
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)
235 236 237
{
  dt_iop_bilat_params_t *p = (dt_iop_bilat_params_t *)p1;
  dt_iop_bilat_data_t *d = (dt_iop_bilat_data_t *)piece->data;
238
  *d = *p;
239 240

#ifdef HAVE_OPENCL
241 242
  if(d->mode == s_mode_bilateral)
    piece->process_cl_ready = (piece->process_cl_ready && !(darktable.opencl->avoid_atomics));
243
#endif
244 245
  if(d->mode == s_mode_local_laplacian)
    piece->process_tiling_ready = 0; // can't deal with tiles, sorry.
246 247 248
}


249
void init_pipe(struct dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
250
{
251
  piece->data = calloc(1, sizeof(dt_iop_bilat_data_t));
252 253 254 255
  self->commit_params(self, self->default_params, pipe, piece);
}


256
void cleanup_pipe(struct dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
257 258
{
  free(piece->data);
259
  piece->data = NULL;
260 261
}

262

263 264 265 266 267 268 269
#if defined(__SSE2__)
void process_sse2(struct dt_iop_module_t *self, dt_dev_pixelpipe_iop_t *piece, const void *const i, void *const o,
             const dt_iop_roi_t *const roi_in, const dt_iop_roi_t *const roi_out)
{
  // this is called for preview and full pipe separately, each with its own pixelpipe piece.
  // get our data struct:
  dt_iop_bilat_data_t *d = (dt_iop_bilat_data_t *)piece->data;
270
  dt_iop_bilat_gui_data_t *g = self->gui_data;
271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286
  // the total scale is composed of scale before input to the pipeline (iscale),
  // and the scale of the roi.
  const float scale = piece->iscale / roi_in->scale;
  const float sigma_r = d->sigma_r; // does not depend on scale
  const float sigma_s = d->sigma_s / scale;

  if(d->mode == s_mode_bilateral)
  {
    dt_bilateral_t *b = dt_bilateral_init(roi_in->width, roi_in->height, sigma_s, sigma_r);
    dt_bilateral_splat(b, (float *)i);
    dt_bilateral_blur(b);
    dt_bilateral_slice(b, (float *)i, (float *)o, d->detail);
    dt_bilateral_free(b);
  }
  else // s_mode_local_laplacian
  {
287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302
    local_laplacian_boundary_t b = {0};
    if(self->dev->gui_attached && g && piece->pipe->type == DT_DEV_PIXELPIPE_PREVIEW)
    {
      b.mode = 1;
    }
    else if(self->dev->gui_attached && g && piece->pipe->type == DT_DEV_PIXELPIPE_FULL)
    {
      // full pipeline working on ROI needs boundary conditions from preview pipe
      // only do this if roi covers less than 90% of full width
      if(MIN(roi_in->width/roi_in->scale / piece->buf_in.width,
             roi_in->height/roi_in->scale / piece->buf_in.height) < 0.9)
      {
        dt_pthread_mutex_lock(&g->lock);
        const uint64_t hash = g->hash;
        dt_pthread_mutex_unlock(&g->lock);
        if(hash != 0 && !dt_dev_sync_pixelpipe_hash(self->dev, piece->pipe, 0, self->priority, &g->lock, &g->hash))
303
        {
304 305
          // TODO: remove this debug output at some point:
          dt_control_log(_("local laplacian: inconsistent output"));
306 307 308 309 310 311 312 313 314
        }
        else
        {
          dt_pthread_mutex_lock(&g->lock);
          // grab preview pipe buffers here:
          b = g->ll_boundary;
          dt_pthread_mutex_unlock(&g->lock);
          if(b.wd > 0 && b.ht > 0) b.mode = 2;
        }
315 316 317 318 319
      }
    }

    b.roi = roi_in;
    b.buf = &piece->buf_in;
320 321
    // also lock the ll_boundary in case we're using it.
    // could get away without this if the preview pipe didn't also free the data below.
322 323
    const int lockit = self->dev->gui_attached && g && piece->pipe->type == DT_DEV_PIXELPIPE_FULL;
    if(lockit) dt_pthread_mutex_lock(&g->lock);
324
    local_laplacian_sse2(i, o, roi_in->width, roi_in->height, d->midtone, d->sigma_s, d->sigma_r, d->detail, &b);
325
    if(lockit) dt_pthread_mutex_unlock(&g->lock);
326 327 328 329 330 331 332 333 334 335 336 337

    // preview pixelpipe stores values.
    if(self->dev->gui_attached && g && piece->pipe->type == DT_DEV_PIXELPIPE_PREVIEW)
    {
      uint64_t hash = dt_dev_hash_plus(self->dev, piece->pipe, 0, self->priority);
      dt_pthread_mutex_lock(&g->lock);
      // store buffer pointers on gui struct. maybe need to swap/free old ones
      local_laplacian_boundary_free(&g->ll_boundary);
      g->ll_boundary = b;
      g->hash = hash;
      dt_pthread_mutex_unlock(&g->lock);
    }
338
  }
339

340
  if(piece->pipe->mask_display & DT_DEV_PIXELPIPE_DISPLAY_MASK) dt_iop_alpha_copy(i, o, roi_in->width, roi_in->height);
341 342 343
}
#endif

344 345
void process(struct dt_iop_module_t *self, dt_dev_pixelpipe_iop_t *piece, const void *const i, void *const o,
             const dt_iop_roi_t *const roi_in, const dt_iop_roi_t *const roi_out)
346 347 348
{
  // this is called for preview and full pipe separately, each with its own pixelpipe piece.
  // get our data struct:
349
  dt_iop_bilat_data_t *d = (dt_iop_bilat_data_t *)piece->data;
350 351
  // the total scale is composed of scale before input to the pipeline (iscale),
  // and the scale of the roi.
352
  const float scale = piece->iscale / roi_in->scale;
353
  const float sigma_r = d->sigma_r; // does not depend on scale
354 355
  const float sigma_s = d->sigma_s / scale;

356 357 358 359 360 361 362 363 364 365
  if(d->mode == s_mode_bilateral)
  {
    dt_bilateral_t *b = dt_bilateral_init(roi_in->width, roi_in->height, sigma_s, sigma_r);
    dt_bilateral_splat(b, (float *)i);
    dt_bilateral_blur(b);
    dt_bilateral_slice(b, (float *)i, (float *)o, d->detail);
    dt_bilateral_free(b);
  }
  else // s_mode_local_laplacian
  {
366
    local_laplacian(i, o, roi_in->width, roi_in->height, d->midtone, d->sigma_s, d->sigma_r, d->detail, 0);
367
  }
368

369
  if(piece->pipe->mask_display & DT_DEV_PIXELPIPE_DISPLAY_MASK) dt_iop_alpha_copy(i, o, roi_in->width, roi_in->height);
370 371 372 373 374
}

/** init, cleanup, commit to pipeline */
void init(dt_iop_module_t *module)
{
375 376
  module->params = calloc(1, sizeof(dt_iop_bilat_params_t));
  module->default_params = calloc(1, sizeof(dt_iop_bilat_params_t));
377 378 379 380
  // our module is disabled by default
  // by default:
  module->default_enabled = 0;
  // order has to be changed by editing the dependencies in tools/iop_dependencies.py
Heiko Bauke's avatar
Heiko Bauke committed
381
  module->priority = 588; // module order created by iop_dependencies.py, do not edit!
382
  module->params_size = sizeof(dt_iop_bilat_params_t);
383 384
  module->gui_data = NULL;
  // init defaults:
385
  dt_iop_bilat_params_t tmp = (dt_iop_bilat_params_t){ s_mode_local_laplacian, 1.0, 1.0, 0.2, 0.2 };
386

387 388
  memcpy(module->params, &tmp, sizeof(dt_iop_bilat_params_t));
  memcpy(module->default_params, &tmp, sizeof(dt_iop_bilat_params_t));
389 390 391 392 393 394 395 396
}

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

397
static void spatial_callback(GtkWidget *w, dt_iop_module_t *self)
398
{
399
  dt_iop_bilat_params_t *p = (dt_iop_bilat_params_t *)self->params;
400 401 402 403
  p->sigma_s = dt_bauhaus_slider_get(w);
  dt_dev_add_history_item(darktable.develop, self, TRUE);
}

404
static void range_callback(GtkWidget *w, dt_iop_module_t *self)
405
{
406
  dt_iop_bilat_params_t *p = (dt_iop_bilat_params_t *)self->params;
407 408 409 410
  p->sigma_r = dt_bauhaus_slider_get(w);
  dt_dev_add_history_item(darktable.develop, self, TRUE);
}

411 412 413
static void highlights_callback(GtkWidget *w, dt_iop_module_t *self)
{
  dt_iop_bilat_params_t *p = (dt_iop_bilat_params_t *)self->params;
414
  p->sigma_r = dt_bauhaus_slider_get(w)/100.0f;
415 416 417 418 419 420
  dt_dev_add_history_item(darktable.develop, self, TRUE);
}

static void shadows_callback(GtkWidget *w, dt_iop_module_t *self)
{
  dt_iop_bilat_params_t *p = (dt_iop_bilat_params_t *)self->params;
421
  p->sigma_s = dt_bauhaus_slider_get(w)/100.0f;
422 423 424
  dt_dev_add_history_item(darktable.develop, self, TRUE);
}

425 426 427 428 429 430 431
static void midtone_callback(GtkWidget *w, dt_iop_module_t *self)
{
  dt_iop_bilat_params_t *p = self->params;
  p->midtone = dt_bauhaus_slider_get(w);
  dt_dev_add_history_item(darktable.develop, self, TRUE);
}

432
static void detail_callback(GtkWidget *w, dt_iop_module_t *self)
433
{
434
  dt_iop_bilat_params_t *p = (dt_iop_bilat_params_t *)self->params;
435
  p->detail = (dt_bauhaus_slider_get(w)-100.0f)/100.0f;
436 437 438
  dt_dev_add_history_item(darktable.develop, self, TRUE);
}

439 440 441 442
static void mode_callback(GtkWidget *w, dt_iop_module_t *self)
{
  dt_iop_bilat_params_t *p = (dt_iop_bilat_params_t *)self->params;
  p->mode = dt_bauhaus_combobox_get(w);
443 444 445
  dt_iop_bilat_gui_data_t *g = (dt_iop_bilat_gui_data_t *)self->gui_data;
  if(p->mode == s_mode_local_laplacian)
  {
446 447
    gtk_widget_set_visible(g->highlights, TRUE);
    gtk_widget_set_visible(g->shadows, TRUE);
448
    gtk_widget_set_visible(g->midtone, TRUE);
449 450
    gtk_widget_set_visible(g->range, FALSE);
    gtk_widget_set_visible(g->spatial, FALSE);
451 452
    dt_bauhaus_slider_set(g->highlights, 100.0f);
    dt_bauhaus_slider_set(g->shadows, 100.0f);
453 454 455
  }
  else
  {
456 457
    gtk_widget_set_visible(g->highlights, FALSE);
    gtk_widget_set_visible(g->shadows, FALSE);
458
    gtk_widget_set_visible(g->midtone, FALSE);
459 460
    gtk_widget_set_visible(g->range, TRUE);
    gtk_widget_set_visible(g->spatial, TRUE);
461 462
    dt_bauhaus_slider_set(g->range, 20.0f);
    dt_bauhaus_slider_set(g->spatial, 50.0f);
463
  }
464 465 466
  dt_dev_add_history_item(darktable.develop, self, TRUE);
}

467 468 469 470
/** gui callbacks, these are needed. */
void gui_update(dt_iop_module_t *self)
{
  // let gui slider match current parameters:
471 472
  dt_iop_bilat_gui_data_t *g = (dt_iop_bilat_gui_data_t *)self->gui_data;
  dt_iop_bilat_params_t *p = (dt_iop_bilat_params_t *)self->params;
473
  dt_bauhaus_slider_set(g->detail, 100.0f*p->detail+100.0f);
474
  dt_bauhaus_combobox_set(g->mode, p->mode);
475 476
  if(p->mode == s_mode_local_laplacian)
  {
477 478
    dt_bauhaus_slider_set(g->shadows, p->sigma_s*100.0f);
    dt_bauhaus_slider_set(g->highlights, p->sigma_r*100.0f);
479
    dt_bauhaus_slider_set(g->midtone, p->midtone);
480 481
    gtk_widget_set_visible(g->range, FALSE);
    gtk_widget_set_visible(g->spatial, FALSE);
482 483
    gtk_widget_set_visible(g->highlights, TRUE);
    gtk_widget_set_visible(g->shadows, TRUE);
484
    gtk_widget_set_visible(g->midtone, TRUE);
485 486 487
    dt_pthread_mutex_lock(&g->lock);
    g->hash = 0;
    dt_pthread_mutex_unlock(&g->lock);
488 489 490
  }
  else
  {
491 492
    dt_bauhaus_slider_set(g->spatial, p->sigma_s);
    dt_bauhaus_slider_set(g->range, p->sigma_r);
493 494
    gtk_widget_set_visible(g->range, TRUE);
    gtk_widget_set_visible(g->spatial, TRUE);
495 496
    gtk_widget_set_visible(g->highlights, FALSE);
    gtk_widget_set_visible(g->shadows, FALSE);
497
    gtk_widget_set_visible(g->midtone, FALSE);
498
  }
499 500 501 502 503
}

void gui_init(dt_iop_module_t *self)
{
  // init the slider (more sophisticated layouts are possible with gtk tables and boxes):
504 505
  self->gui_data = malloc(sizeof(dt_iop_bilat_gui_data_t));
  dt_iop_bilat_gui_data_t *g = (dt_iop_bilat_gui_data_t *)self->gui_data;
506 507 508
  memset(&g->ll_boundary, 0, sizeof(local_laplacian_boundary_t));
  dt_pthread_mutex_init(&g->lock, NULL);
  g->hash = 0;
509
  self->widget = gtk_box_new(GTK_ORIENTATION_VERTICAL, DT_BAUHAUS_SPACE);
510

511 512 513
  g->mode = dt_bauhaus_combobox_new(self);
  dt_bauhaus_widget_set_label(g->mode, NULL, _("mode"));
  gtk_box_pack_start(GTK_BOX(self->widget), g->mode, TRUE, TRUE, 0);
514
  dt_bauhaus_combobox_add(g->mode, _("bilateral grid"));
515
  dt_bauhaus_combobox_add(g->mode, _("local laplacian filter"));
516 517
  dt_bauhaus_combobox_set_default(g->mode, s_mode_local_laplacian);
  dt_bauhaus_combobox_set(g->mode, s_mode_local_laplacian);
518
  gtk_widget_set_tooltip_text(g->mode, _("the filter used for local contrast enhancement. bilateral is faster but can lead to artifacts around edges for extreme settings."));
519

520
  g->detail = dt_bauhaus_slider_new_with_range(self, 0.0, 500.0, 1.0, 100.0, 0);
521 522
  gtk_box_pack_start(GTK_BOX(self->widget), g->detail, TRUE, TRUE, 0);
  dt_bauhaus_widget_set_label(g->detail, NULL, _("detail"));
523
  dt_bauhaus_slider_set_format(g->detail, "%.0f%%");
524
  gtk_widget_set_tooltip_text(g->detail, _("changes the local contrast"));
525

526
  g->spatial = dt_bauhaus_slider_new_with_range(self, 1, 100, 1, 50, 0);
527
  dt_bauhaus_widget_set_label(g->spatial, NULL, _("coarseness"));
528
  gtk_box_pack_start(GTK_BOX(self->widget), g->spatial, TRUE, TRUE, 0);
529
  gtk_widget_set_tooltip_text(g->spatial, _("feature size of local details (spatial sigma of bilateral filter)"));
530

531
  g->range = dt_bauhaus_slider_new_with_range(self, 1, 100, 1, 20, 0);
532
  gtk_box_pack_start(GTK_BOX(self->widget), g->range, TRUE, TRUE, 0);
533
  dt_bauhaus_widget_set_label(g->range, NULL, _("contrast"));
534
  gtk_widget_set_tooltip_text(g->range, _("L difference to detect edges (range sigma of bilateral filter)"));
535

536
  g->highlights = dt_bauhaus_slider_new_with_range(self, 0.0, 200.0, 1.0, 100.0, 0);
537
  gtk_box_pack_start(GTK_BOX(self->widget), g->highlights, TRUE, TRUE, 0);
538 539
  dt_bauhaus_widget_set_label(g->highlights, NULL, _("highlights"));
  dt_bauhaus_slider_set_format(g->highlights, "%.0f%%");
540
  gtk_widget_set_tooltip_text(g->highlights, _("changes the local contrast of highlights"));
541

542
  g->shadows = dt_bauhaus_slider_new_with_range(self, 0.0, 200.0, 1.0, 100.0, 0);
543
  gtk_box_pack_start(GTK_BOX(self->widget), g->shadows, TRUE, TRUE, 0);
544
  dt_bauhaus_widget_set_label(g->shadows, NULL, _("shadows"));
545
  gtk_widget_set_tooltip_text(g->shadows, _("changes the local contrast of shadows"));
546
  dt_bauhaus_slider_set_format(g->shadows, "%.0f%%");
547

548 549 550 551
  g->midtone = dt_bauhaus_slider_new_with_range(self, 0.001, 1.0, 0.001, 0.2, 3);
  gtk_box_pack_start(GTK_BOX(self->widget), g->midtone, TRUE, TRUE, 0);
  dt_bauhaus_widget_set_label(g->midtone, NULL, _("midtone range"));
  gtk_widget_set_tooltip_text(g->midtone, _("defines what counts as midtones. lower for better dynamic range compression (reduce shadow and highlight contrast), increase for more powerful local contrast"));
552

553 554 555 556 557 558 559
  // work around multi-instance issue which calls show all a fair bit:
  g_object_set(G_OBJECT(g->highlights), "no-show-all", TRUE, NULL);
  g_object_set(G_OBJECT(g->shadows), "no-show-all", TRUE, NULL);
  g_object_set(G_OBJECT(g->midtone), "no-show-all", TRUE, NULL);
  g_object_set(G_OBJECT(g->range), "no-show-all", TRUE, NULL);
  g_object_set(G_OBJECT(g->spatial), "no-show-all", TRUE, NULL);

560 561 562
  g_signal_connect(G_OBJECT(g->spatial), "value-changed", G_CALLBACK(spatial_callback), self);
  g_signal_connect(G_OBJECT(g->range), "value-changed", G_CALLBACK(range_callback), self);
  g_signal_connect(G_OBJECT(g->detail), "value-changed", G_CALLBACK(detail_callback), self);
563 564
  g_signal_connect(G_OBJECT(g->highlights), "value-changed", G_CALLBACK(highlights_callback), self);
  g_signal_connect(G_OBJECT(g->shadows), "value-changed", G_CALLBACK(shadows_callback), self);
565
  g_signal_connect(G_OBJECT(g->midtone), "value-changed", G_CALLBACK(midtone_callback), self);
566
  g_signal_connect(G_OBJECT(g->mode), "value-changed", G_CALLBACK(mode_callback), self);
567 568 569 570 571
}

void gui_cleanup(dt_iop_module_t *self)
{
  // nothing else necessary, gtk will clean up the slider.
572 573 574
  dt_iop_bilat_gui_data_t *g = (dt_iop_bilat_gui_data_t *)self->gui_data;
  local_laplacian_boundary_free(&g->ll_boundary);
  dt_pthread_mutex_destroy(&g->lock);
575 576 577 578 579 580
  free(self->gui_data);
  self->gui_data = NULL;
}

// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.sh
// vim: shiftwidth=2 expandtab tabstop=2 cindent
Tobias Ellinghaus's avatar
Tobias Ellinghaus committed
581
// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;