hotpixels.c 17.2 KB
Newer Older
Rostyslav Pidgornyi's avatar
Rostyslav Pidgornyi committed
1 2 3
/*
    This file is part of darktable,
    copyright (c) 2011 Rostyslav Pidgornyi
4
    copyright (c) 2012 Henrik Andersson
5

6 7 8
    and the initial plugin `stuck pixels' was
    copyright (c) 2011 bruce guenter

Rostyslav Pidgornyi's avatar
Rostyslav Pidgornyi committed
9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
    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
26
#include "bauhaus/bauhaus.h"
Rostyslav Pidgornyi's avatar
Rostyslav Pidgornyi committed
27 28
#include "control/control.h"
#include "develop/imageop.h"
29
#include "develop/imageop_math.h"
Rostyslav Pidgornyi's avatar
Rostyslav Pidgornyi committed
30
#include "dtgtk/resetlabel.h"
31
#include "gui/accelerators.h"
Rostyslav Pidgornyi's avatar
Rostyslav Pidgornyi committed
32
#include "gui/gtk.h"
33
#include "iop/iop_api.h"
34

Rostyslav Pidgornyi's avatar
Rostyslav Pidgornyi committed
35 36 37
#include <gtk/gtk.h>
#include <stdlib.h>

38
DT_MODULE_INTROSPECTION(1, dt_iop_hotpixels_params_t)
Rostyslav Pidgornyi's avatar
Rostyslav Pidgornyi committed
39 40 41 42 43 44 45

typedef struct dt_iop_hotpixels_params_t
{
  float strength;
  float threshold;
  gboolean markfixed;
  gboolean permissive;
46
} dt_iop_hotpixels_params_t;
Rostyslav Pidgornyi's avatar
Rostyslav Pidgornyi committed
47 48 49

typedef struct dt_iop_hotpixels_gui_data_t
{
50
  GtkWidget *box_raw;
51
  GtkWidget *threshold, *strength;
Rostyslav Pidgornyi's avatar
Rostyslav Pidgornyi committed
52 53 54
  GtkToggleButton *markfixed;
  GtkToggleButton *permissive;
  GtkLabel *message;
55
  int pixels_fixed;
56
  GtkWidget *label_non_raw;
57
} dt_iop_hotpixels_gui_data_t;
Rostyslav Pidgornyi's avatar
Rostyslav Pidgornyi committed
58 59 60 61 62 63 64 65

typedef struct dt_iop_hotpixels_data_t
{
  uint32_t filters;
  float threshold;
  float multiplier;
  gboolean permissive;
  gboolean markfixed;
66
} dt_iop_hotpixels_data_t;
Rostyslav Pidgornyi's avatar
Rostyslav Pidgornyi committed
67 68 69 70 71 72

const char *name()
{
  return _("hot pixels");
}

73
int groups()
Rostyslav Pidgornyi's avatar
Rostyslav Pidgornyi committed
74
{
75
  return IOP_GROUP_CORRECT;
Rostyslav Pidgornyi's avatar
Rostyslav Pidgornyi committed
76 77
}

78
int flags()
79
{
80
  return IOP_FLAGS_SUPPORTS_BLENDING | IOP_FLAGS_ONE_INSTANCE;
81 82
}

83
void init_key_accels(dt_iop_module_so_t *self)
84
{
85 86
  dt_accel_register_slider_iop(self, FALSE, NC_("accel", "threshold"));
  dt_accel_register_slider_iop(self, FALSE, NC_("accel", "strength"));
87
}
88 89 90

void connect_key_accels(dt_iop_module_t *self)
{
91
  dt_iop_hotpixels_gui_data_t *g = (dt_iop_hotpixels_gui_data_t *)self->gui_data;
92 93 94 95 96

  dt_accel_connect_slider_iop(self, "threshold", GTK_WIDGET(g->threshold));
  dt_accel_connect_slider_iop(self, "strength", GTK_WIDGET(g->strength));
}

97 98 99 100 101 102 103 104
/* Detect hot sensor pixels based on the 4 surrounding sites. Pixels
 * having 3 or 4 (depending on permissive setting) surrounding pixels that
 * than value*multiplier are considered "hot", and are replaced by the maximum of
 * the neighbour pixels. The permissive variant allows for
 * correcting pairs of hot pixels in adjacent sites. Replacement using
 * the maximum produces fewer artifacts when inadvertently replacing
 * non-hot pixels.
 * This is the Bayer sensor variant. */
105 106 107
static int process_bayer(const dt_iop_hotpixels_data_t *data,
                         const void *const ivoid, void *const ovoid,
                         const dt_iop_roi_t *const roi_out)
108
{
109 110 111 112
  const float threshold = data->threshold;
  const float multiplier = data->multiplier;
  const gboolean markfixed = data->markfixed;
  const int min_neighbours = data->permissive ? 3 : 4;
113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161
  const int width = roi_out->width;
  const int widthx2 = width * 2;
  int fixed = 0;

#ifdef _OPENMP
#pragma omp parallel for default(none) reduction(+ : fixed) schedule(static)
#endif
  for(int row = 2; row < roi_out->height - 2; row++)
  {
    const float *in = (float *)ivoid + (size_t)width * row + 2;
    float *out = (float *)ovoid + (size_t)width * row + 2;
    for(int col = 2; col < width - 2; col++, in++, out++)
    {
      float mid = *in * multiplier;
      if(*in > threshold)
      {
        int count = 0;
        float maxin = 0.0;
        float other;
#define TESTONE(OFFSET)                                                                                      \
  other = in[OFFSET];                                                                                        \
  if(mid > other)                                                                                            \
  {                                                                                                          \
    count++;                                                                                                 \
    if(other > maxin) maxin = other;                                                                         \
  }
        TESTONE(-2);
        TESTONE(-widthx2);
        TESTONE(+2);
        TESTONE(+widthx2);
#undef TESTONE
        if(count >= min_neighbours)
        {
          *out = maxin;
          fixed++;
          if(markfixed)
          {
            for(int i = -2; i >= -10 && i >= -col; i -= 2) out[i] = *in;
            for(int i = 2; i <= 10 && i < width - col; i += 2) out[i] = *in;
          }
        }
      }
    }
  }

  return fixed;
}

/* X-Trans sensor equivalent of process_bayer(). */
162 163 164
static int process_xtrans(const dt_iop_hotpixels_data_t *data,
                          const void *const ivoid, void *const ovoid,
                          const dt_iop_roi_t *const roi_out, const uint8_t (*const xtrans)[6])
Dan Torop's avatar
Dan Torop committed
165
{
166 167
  // for each cell of sensor array, pre-calculate, a list of the x/y
  // offsets of the four radially nearest pixels of the same color
Dan Torop's avatar
Dan Torop committed
168
  int offsets[6][6][4][2];
169
  // increasing offsets from pixel to find nearest like-colored pixels
170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190
  const int search[20][2] = { { -1, 0 },
                              { 1, 0 },
                              { 0, -1 },
                              { 0, 1 },
                              { -1, -1 },
                              { -1, 1 },
                              { 1, -1 },
                              { 1, 1 },
                              { -2, 0 },
                              { 2, 0 },
                              { 0, -2 },
                              { 0, 2 },
                              { -2, -1 },
                              { -2, 1 },
                              { 2, -1 },
                              { 2, 1 },
                              { -1, -2 },
                              { 1, -2 },
                              { -1, 2 },
                              { 1, 2 } };
  for(int j = 0; j < 6; ++j)
191
  {
192
    for(int i = 0; i < 6; ++i)
Dan Torop's avatar
Dan Torop committed
193
    {
194
      const uint8_t c = FCxtrans(j, i, roi_out, xtrans);
195
      for(int s = 0, found = 0; s < 20 && found < 4; ++s)
Dan Torop's avatar
Dan Torop committed
196
      {
197
        if(c == FCxtrans(j + search[s][1], i + search[s][0], roi_out, xtrans))
Dan Torop's avatar
Dan Torop committed
198
        {
199 200
          offsets[j][i][found][0] = search[s][0];
          offsets[j][i][found][1] = search[s][1];
Dan Torop's avatar
Dan Torop committed
201 202 203 204
          ++found;
        }
      }
    }
205
  }
Dan Torop's avatar
Dan Torop committed
206

207 208 209 210
  const float threshold = data->threshold;
  const float multiplier = data->multiplier;
  const gboolean markfixed = data->markfixed;
  const int min_neighbours = data->permissive ? 3 : 4;
211
  const int width = roi_out->width;
Dan Torop's avatar
Dan Torop committed
212
  int fixed = 0;
213

Dan Torop's avatar
Dan Torop committed
214
#ifdef _OPENMP
215
#pragma omp parallel for default(none) shared(offsets) reduction(+ : fixed) schedule(static)
Dan Torop's avatar
Dan Torop committed
216
#endif
217
  for(int row = 2; row < roi_out->height - 2; row++)
Dan Torop's avatar
Dan Torop committed
218
  {
219 220
    const float *in = (float *)ivoid + (size_t)width * row + 2;
    float *out = (float *)ovoid + (size_t)width * row + 2;
221
    for(int col = 2; col < width - 2; col++, in++, out++)
Dan Torop's avatar
Dan Torop committed
222
    {
223 224
      float mid = *in * multiplier;
      if(*in > threshold)
Dan Torop's avatar
Dan Torop committed
225
      {
226 227 228
        int count = 0;
        float maxin = 0.0;
        for(int n = 0; n < 4; ++n)
Dan Torop's avatar
Dan Torop committed
229
        {
230 231 232
          int xx = offsets[row % 6][col % 6][n][0];
          int yy = offsets[row % 6][col % 6][n][1];
          float other = *(in + xx + yy * (size_t)width);
233
          if(mid > other)
Dan Torop's avatar
Dan Torop committed
234 235
          {
            count++;
236
            if(other > maxin) maxin = other;
Dan Torop's avatar
Dan Torop committed
237 238 239
          }
        }
        // NOTE: it seems that detecting by 2 neighbors would help for extreme cases
240
        if(count >= min_neighbours)
Dan Torop's avatar
Dan Torop committed
241 242 243
        {
          *out = maxin;
          fixed++;
244
          if(markfixed)
Dan Torop's avatar
Dan Torop committed
245
          {
246 247 248 249 250 251 252 253 254 255 256 257 258 259 260
            const uint8_t c = FCxtrans(row, col, roi_out, xtrans);
            for(int i = -2; i >= -10 && i >= -col; --i)
            {
              if(c == FCxtrans(row, col+i, roi_out, xtrans))
              {
                out[i] = *in;
              }
            }
            for(int i = 2; i <= 10 && i < width - col; ++i)
            {
              if(c == FCxtrans(row, col+i, roi_out, xtrans))
              {
                out[i] = *in;
              }
            }
Dan Torop's avatar
Dan Torop committed
261 262 263 264 265 266 267 268 269
          }
        }
      }
    }
  }

  return fixed;
}

270 271
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)
Rostyslav Pidgornyi's avatar
Rostyslav Pidgornyi committed
272 273 274 275
{
  dt_iop_hotpixels_gui_data_t *g = (dt_iop_hotpixels_gui_data_t *)self->gui_data;
  const dt_iop_hotpixels_data_t *data = (dt_iop_hotpixels_data_t *)piece->data;

276 277
  // The processing loop should output only a few pixels, so just copy everything first
  memcpy(ovoid, ivoid, (size_t)roi_out->width * roi_out->height * sizeof(float));
Dan Torop's avatar
Dan Torop committed
278

279
  int fixed;
280
  if(piece->pipe->dsc.filters == 9u)
Dan Torop's avatar
Dan Torop committed
281
  {
282
    fixed = process_xtrans(data, ivoid, ovoid, roi_out, (const uint8_t(*const)[6])piece->pipe->dsc.xtrans);
Dan Torop's avatar
Dan Torop committed
283
  }
284
  else
Rostyslav Pidgornyi's avatar
Rostyslav Pidgornyi committed
285
  {
286
    fixed = process_bayer(data, ivoid, ovoid, roi_out);
Rostyslav Pidgornyi's avatar
Rostyslav Pidgornyi committed
287
  }
288 289

  if(g != NULL && self->dev->gui_attached && piece->pipe->type == DT_DEV_PIXELPIPE_FULL)
Rostyslav Pidgornyi's avatar
Rostyslav Pidgornyi committed
290
  {
291
    g->pixels_fixed = fixed;
Rostyslav Pidgornyi's avatar
Rostyslav Pidgornyi committed
292 293 294
  }
}

295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313
void reload_defaults(dt_iop_module_t *module)
{
  const dt_iop_hotpixels_params_t tmp
      = {.strength = 0.25, .threshold = 0.05, .markfixed = FALSE, .permissive = FALSE };

  // we might be called from presets update infrastructure => there is no image
  if(!module->dev) goto end;

  // can't be switched on for non-raw images:
  if(dt_image_is_raw(&module->dev->image_storage))
    module->hide_enable_button = 0;
  else
    module->hide_enable_button = 1;

end:
  memcpy(module->params, &tmp, sizeof(dt_iop_hotpixels_params_t));
  memcpy(module->default_params, &tmp, sizeof(dt_iop_hotpixels_params_t));
}

Rostyslav Pidgornyi's avatar
Rostyslav Pidgornyi committed
314 315 316
void init(dt_iop_module_t *module)
{
  module->data = NULL;
317 318
  module->params = calloc(1, sizeof(dt_iop_hotpixels_params_t));
  module->default_params = calloc(1, sizeof(dt_iop_hotpixels_params_t));
Rostyslav Pidgornyi's avatar
Rostyslav Pidgornyi committed
319
  module->default_enabled = 0;
Heiko Bauke's avatar
Heiko Bauke committed
320
  module->priority = 88; // module order created by iop_dependencies.py, do not edit!
Rostyslav Pidgornyi's avatar
Rostyslav Pidgornyi committed
321 322 323 324 325 326 327 328 329 330 331 332
  module->params_size = sizeof(dt_iop_hotpixels_params_t);
  module->gui_data = NULL;
}

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

333 334
void commit_params(struct dt_iop_module_t *self, dt_iop_params_t *params, dt_dev_pixelpipe_t *pipe,
                   dt_dev_pixelpipe_iop_t *piece)
Rostyslav Pidgornyi's avatar
Rostyslav Pidgornyi committed
335 336 337
{
  dt_iop_hotpixels_params_t *p = (dt_iop_hotpixels_params_t *)params;
  dt_iop_hotpixels_data_t *d = (dt_iop_hotpixels_data_t *)piece->data;
338
  d->filters = piece->pipe->dsc.filters;
339
  d->multiplier = p->strength / 2.0;
Rostyslav Pidgornyi's avatar
Rostyslav Pidgornyi committed
340 341
  d->threshold = p->threshold;
  d->permissive = p->permissive;
342 343
  d->markfixed = p->markfixed && (pipe->type != DT_DEV_PIXELPIPE_EXPORT)
                 && (pipe->type != DT_DEV_PIXELPIPE_THUMBNAIL);
344
  if(!(pipe->image.flags & DT_IMAGE_RAW) || p->strength == 0.0) piece->enabled = 0;
Rostyslav Pidgornyi's avatar
Rostyslav Pidgornyi committed
345 346
}

347
void init_pipe(struct dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
Rostyslav Pidgornyi's avatar
Rostyslav Pidgornyi committed
348 349 350 351 352
{
  piece->data = malloc(sizeof(dt_iop_hotpixels_data_t));
  self->commit_params(self, self->default_params, pipe, piece);
}

353
void cleanup_pipe(struct dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
Rostyslav Pidgornyi's avatar
Rostyslav Pidgornyi committed
354 355
{
  free(piece->data);
356
  piece->data = NULL;
Rostyslav Pidgornyi's avatar
Rostyslav Pidgornyi committed
357 358
}

359
static void strength_callback(GtkRange *range, dt_iop_module_t *self)
Rostyslav Pidgornyi's avatar
Rostyslav Pidgornyi committed
360 361 362 363
{
  if(darktable.gui->reset) return;
  dt_iop_hotpixels_gui_data_t *g = (dt_iop_hotpixels_gui_data_t *)self->gui_data;
  dt_iop_hotpixels_params_t *p = (dt_iop_hotpixels_params_t *)self->params;
364
  p->strength = dt_bauhaus_slider_get(g->strength);
Rostyslav Pidgornyi's avatar
Rostyslav Pidgornyi committed
365 366 367
  dt_dev_add_history_item(darktable.develop, self, TRUE);
}

368
static void threshold_callback(GtkRange *range, dt_iop_module_t *self)
Rostyslav Pidgornyi's avatar
Rostyslav Pidgornyi committed
369 370 371 372
{
  if(darktable.gui->reset) return;
  dt_iop_hotpixels_gui_data_t *g = (dt_iop_hotpixels_gui_data_t *)self->gui_data;
  dt_iop_hotpixels_params_t *p = (dt_iop_hotpixels_params_t *)self->params;
373
  p->threshold = dt_bauhaus_slider_get(g->threshold);
Rostyslav Pidgornyi's avatar
Rostyslav Pidgornyi committed
374 375 376
  dt_dev_add_history_item(darktable.develop, self, TRUE);
}

377
static void markfixed_callback(GtkRange *range, dt_iop_module_t *self)
Rostyslav Pidgornyi's avatar
Rostyslav Pidgornyi committed
378 379 380 381 382 383 384 385
{
  if(darktable.gui->reset) return;
  dt_iop_hotpixels_gui_data_t *g = (dt_iop_hotpixels_gui_data_t *)self->gui_data;
  dt_iop_hotpixels_params_t *p = (dt_iop_hotpixels_params_t *)self->params;
  p->markfixed = gtk_toggle_button_get_active(g->markfixed);
  dt_dev_add_history_item(darktable.develop, self, TRUE);
}

386
static void permissive_callback(GtkRange *range, dt_iop_module_t *self)
Rostyslav Pidgornyi's avatar
Rostyslav Pidgornyi committed
387 388 389 390 391 392 393 394
{
  if(darktable.gui->reset) return;
  dt_iop_hotpixels_gui_data_t *g = (dt_iop_hotpixels_gui_data_t *)self->gui_data;
  dt_iop_hotpixels_params_t *p = (dt_iop_hotpixels_params_t *)self->params;
  p->permissive = gtk_toggle_button_get_active(g->permissive);
  dt_dev_add_history_item(darktable.develop, self, TRUE);
}

395
void gui_update(dt_iop_module_t *self)
Rostyslav Pidgornyi's avatar
Rostyslav Pidgornyi committed
396 397 398
{
  dt_iop_hotpixels_gui_data_t *g = (dt_iop_hotpixels_gui_data_t *)self->gui_data;
  dt_iop_hotpixels_params_t *p = (dt_iop_hotpixels_params_t *)self->params;
399 400
  dt_bauhaus_slider_set(g->strength, p->strength);
  dt_bauhaus_slider_set(g->threshold, p->threshold);
Rostyslav Pidgornyi's avatar
Rostyslav Pidgornyi committed
401 402
  gtk_toggle_button_set_active(g->markfixed, p->markfixed);
  gtk_toggle_button_set_active(g->permissive, p->permissive);
403 404
  g->pixels_fixed = -1;
  gtk_label_set_text(g->message, "");
405 406 407 408 409 410 411 412 413 414 415

  if(!self->hide_enable_button)
  {
    gtk_widget_show(g->box_raw);
    gtk_widget_hide(g->label_non_raw);
  }
  else
  {
    gtk_widget_hide(g->box_raw);
    gtk_widget_show(g->label_non_raw);
  }
416 417
}

418
static gboolean draw(GtkWidget *widget, cairo_t *cr, dt_iop_module_t *self)
419 420 421 422 423 424
{
  dt_iop_hotpixels_gui_data_t *g = (dt_iop_hotpixels_gui_data_t *)self->gui_data;
  if(darktable.gui->reset) return FALSE;

  if(g->pixels_fixed < 0) return FALSE;

425
  char *str = g_strdup_printf(ngettext("fixed %d pixel", "fixed %d pixels", g->pixels_fixed), g->pixels_fixed);
426 427 428
  g->pixels_fixed = -1;

  darktable.gui->reset = 1;
429
  gtk_label_set_text(g->message, str);
430 431
  darktable.gui->reset = 0;

432 433
  g_free(str);

434
  return FALSE;
Rostyslav Pidgornyi's avatar
Rostyslav Pidgornyi committed
435 436
}

437
void gui_init(dt_iop_module_t *self)
Rostyslav Pidgornyi's avatar
Rostyslav Pidgornyi committed
438 439
{
  self->gui_data = malloc(sizeof(dt_iop_hotpixels_gui_data_t));
440 441
  dt_iop_hotpixels_gui_data_t *g = (dt_iop_hotpixels_gui_data_t *)self->gui_data;
  dt_iop_hotpixels_params_t *p = (dt_iop_hotpixels_params_t *)self->params;
442
  g->pixels_fixed = -1;
Rostyslav Pidgornyi's avatar
Rostyslav Pidgornyi committed
443

444
  self->widget = gtk_box_new(GTK_ORIENTATION_VERTICAL, DT_BAUHAUS_SPACE);
445 446 447

  g->box_raw = gtk_box_new(GTK_ORIENTATION_VERTICAL, DT_BAUHAUS_SPACE);
  g_signal_connect(G_OBJECT(g->box_raw), "draw", G_CALLBACK(draw), self);
Rostyslav Pidgornyi's avatar
Rostyslav Pidgornyi committed
448

449 450
  /* threshold */
  g->threshold = dt_bauhaus_slider_new_with_range(self, 0.0, 1.0, 0.005, p->threshold, 4);
451
  dt_bauhaus_slider_set_format(g->threshold, "%.4f");
452
  dt_bauhaus_widget_set_label(g->threshold, NULL, _("threshold"));
453
  gtk_widget_set_tooltip_text(g->threshold, _("lower threshold for hot pixel"));
454
  gtk_box_pack_start(GTK_BOX(g->box_raw), GTK_WIDGET(g->threshold), TRUE, TRUE, 0);
455
  g_signal_connect(G_OBJECT(g->threshold), "value-changed", G_CALLBACK(threshold_callback), self);
Rostyslav Pidgornyi's avatar
Rostyslav Pidgornyi committed
456

457 458
  /* strength */
  g->strength = dt_bauhaus_slider_new_with_range(self, 0.0, 1.0, 0.01, p->strength, 4);
459
  dt_bauhaus_slider_set_format(g->threshold, "%.4f");
460
  dt_bauhaus_widget_set_label(g->strength, NULL, _("strength"));
461
  gtk_widget_set_tooltip_text(g->strength, _("strength of hot pixel correction"));
462
  gtk_box_pack_start(GTK_BOX(g->box_raw), GTK_WIDGET(g->strength), TRUE, TRUE, 0);
463
  g_signal_connect(G_OBJECT(g->strength), "value-changed", G_CALLBACK(strength_callback), self);
Rostyslav Pidgornyi's avatar
Rostyslav Pidgornyi committed
464

465
  /* 3 neighbours */
466
  g->permissive = GTK_TOGGLE_BUTTON(gtk_check_button_new_with_label(_("detect by 3 neighbors")));
Rostyslav Pidgornyi's avatar
Rostyslav Pidgornyi committed
467
  gtk_toggle_button_set_active(g->permissive, p->permissive);
468
  gtk_box_pack_start(GTK_BOX(g->box_raw), GTK_WIDGET(g->permissive), TRUE, TRUE, 0);
Rostyslav Pidgornyi's avatar
Rostyslav Pidgornyi committed
469 470
  g_signal_connect(G_OBJECT(g->permissive), "toggled", G_CALLBACK(permissive_callback), self);

471

472
  GtkBox *hbox1 = GTK_BOX(gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0));
473
  g->markfixed = GTK_TOGGLE_BUTTON(gtk_check_button_new_with_label(_("mark fixed pixels")));
Rostyslav Pidgornyi's avatar
Rostyslav Pidgornyi committed
474 475 476 477
  gtk_toggle_button_set_active(g->markfixed, p->markfixed);
  gtk_box_pack_start(GTK_BOX(hbox1), GTK_WIDGET(g->markfixed), TRUE, TRUE, 0);
  g_signal_connect(G_OBJECT(g->markfixed), "toggled", G_CALLBACK(markfixed_callback), self);

478
  g->message = GTK_LABEL(gtk_label_new("")); // This gets filled in by process
Rostyslav Pidgornyi's avatar
Rostyslav Pidgornyi committed
479 480
  gtk_box_pack_start(GTK_BOX(hbox1), GTK_WIDGET(g->message), TRUE, TRUE, 0);

481 482 483 484 485 486 487
  gtk_box_pack_start(GTK_BOX(g->box_raw), GTK_WIDGET(hbox1), TRUE, TRUE, 0);

  gtk_box_pack_start(GTK_BOX(self->widget), g->box_raw, FALSE, FALSE, 0);

  g->label_non_raw = gtk_label_new(_("hot pixel correction\nonly works for raw images."));
  gtk_widget_set_halign(g->label_non_raw, GTK_ALIGN_START);
  gtk_box_pack_start(GTK_BOX(self->widget), g->label_non_raw, FALSE, FALSE, 0);
Rostyslav Pidgornyi's avatar
Rostyslav Pidgornyi committed
488 489
}

490
void gui_cleanup(dt_iop_module_t *self)
Rostyslav Pidgornyi's avatar
Rostyslav Pidgornyi committed
491 492 493 494 495
{
  free(self->gui_data);
  self->gui_data = NULL;
}

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