monochrome.c 23.8 KB
Newer Older
1 2
/*
    This file is part of darktable,
3
    copyright (c) 2009--2012 johannes hanika.
4 5 6 7 8 9 10 11 12 13 14 15 16 17

    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/>.
*/
18
#ifdef HAVE_CONFIG_H
19
#include "config.h"
20
#endif
21
#include "bauhaus/bauhaus.h"
22 23
#include "common/bilateral.h"
#include "common/bilateralcl.h"
24 25 26
#include "common/colorspaces.h"
#include "common/opencl.h"
#include "control/control.h"
27
#include "develop/develop.h"
28
#include "develop/imageop.h"
29
#include "develop/tiling.h"
30
#include "dtgtk/drawingarea.h"
31
#include "gui/gtk.h"
32
#include "gui/presets.h"
33 34 35 36 37
#include "iop/iop_api.h"
#include <assert.h>
#include <math.h>
#include <stdlib.h>
#include <string.h>
38

39
DT_MODULE_INTROSPECTION(2, dt_iop_monochrome_params_t)
40

41
#define DT_COLORCORRECTION_INSET DT_PIXEL_APPLY_DPI(5)
42
#define DT_COLORCORRECTION_MAX 40.
43
#define PANEL_WIDTH 256.0f
44 45 46

typedef struct dt_iop_monochrome_params_t
{
47
  float a, b, size, highlights;
48
} dt_iop_monochrome_params_t;
49

50 51 52
typedef struct dt_iop_monochrome_data_t
{
  float a, b, size, highlights;
53
} dt_iop_monochrome_data_t;
54

55 56 57
typedef struct dt_iop_monochrome_gui_data_t
{
  GtkDrawingArea *area;
58
  GtkWidget *highlights;
59
  GtkWidget *colorpicker;
60 61
  int dragging;
  cmsHTRANSFORM xform;
62
} dt_iop_monochrome_gui_data_t;
63

64 65
typedef struct dt_iop_monochrome_global_data_t
{
66
  int kernel_monochrome_filter, kernel_monochrome;
67
} dt_iop_monochrome_global_data_t;
68

69 70 71 72 73 74

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

75
int groups()
76
{
77
  return IOP_GROUP_COLOR;
78 79
}

80 81
int flags()
{
82
  return IOP_FLAGS_INCLUDE_IN_STYLES | IOP_FLAGS_SUPPORTS_BLENDING | IOP_FLAGS_ALLOW_TILING;
83
}
84

85 86
int legacy_params(dt_iop_module_t *self, const void *const old_params, const int old_version,
                  void *new_params, const int new_version)
87 88 89 90 91
{
  if(old_version == 1 && new_version == 2)
  {
    dt_iop_monochrome_params_t *p1 = (dt_iop_monochrome_params_t *)old_params;
    dt_iop_monochrome_params_t *p2 = (dt_iop_monochrome_params_t *)new_params;
92
    memcpy(p2, p1, sizeof(dt_iop_monochrome_params_t) - sizeof(float));
93 94 95 96 97 98
    p2->highlights = 0.0f;
    return 0;
  }
  return 1;
}

99
void init_presets(dt_iop_module_so_t *self)
100 101 102 103 104 105 106
{
  dt_iop_monochrome_params_t p;

  p.size = 2.3f;

  p.a = 32.0f;
  p.b = 64.0f;
107
  p.highlights = 0.0f;
108
  dt_gui_presets_add_generic(_("red filter"), self->op, self->version(), &p, sizeof(p), 1);
109

110 111 112
  // p.a = 64.0f;
  // p.b = -32.0f;
  // dt_gui_presets_add_generic(_("purple filter"), self->op, self->version(), &p, sizeof(p), 1);
113

114 115 116
  // p.a = -32.0f;
  // p.b = -64.0f;
  // dt_gui_presets_add_generic(_("blue filter"), self->op, self->version(), &p, sizeof(p), 1);
117

118 119 120
  // p.a = -64.0f;
  // p.b = 32.0f;
  // dt_gui_presets_add_generic(_("green filter"), self->op, self->version(), &p, sizeof(p), 1);
121
}
122

123
static float color_filter(const float ai, const float bi, const float a, const float b, const float size)
124
{
125
  return dt_fast_expf(-CLAMPS(((ai - a) * (ai - a) + (bi - b) * (bi - b)) / (2.0 * size), 0.0f, 1.0f));
126 127
}

128
static float envelope(const float L)
129
{
130
  const float x = CLAMPS(L / 100.0f, 0.0f, 1.0f);
131 132 133 134 135
  // const float alpha = 2.0f;
  const float beta = 0.6f;
  if(x < beta)
  {
    // return 1.0f-fabsf(x/beta-1.0f)^2
136 137
    const float tmp = fabsf(x / beta - 1.0f);
    return 1.0f - tmp * tmp;
138 139 140
  }
  else
  {
141 142 143 144
    const float tmp1 = (1.0f - x) / (1.0f - beta);
    const float tmp2 = tmp1 * tmp1;
    const float tmp3 = tmp2 * tmp1;
    return 3.0f * tmp2 - 2.0f * tmp3;
145 146
  }
}
147

148 149
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)
150
{
151
  dt_iop_monochrome_data_t *d = (dt_iop_monochrome_data_t *)piece->data;
152 153
  const float sigma2 = (d->size * 128.0) * (d->size * 128.0f);
// first pass: evaluate color filter:
154
#ifdef _OPENMP
155
#pragma omp parallel for default(none) shared(d) schedule(static)
156
#endif
157
  for(int k = 0; k < roi_out->height; k++)
158
  {
159 160 161
    const float *in = ((float *)i) + (size_t)4 * k * roi_out->width;
    float *out = ((float *)o) + (size_t)4 * k * roi_out->width;
    for(int j = 0; j < roi_out->width; j++, in += 4, out += 4)
162
    {
163
      out[0] = 100.0f * color_filter(in[1], in[2], d->a, d->b, sigma2);
164 165 166 167 168 169
      out[1] = out[2] = 0.0f;
      out[3] = in[3];
    }
  }

  // second step: blur filter contribution:
170
  const float scale = piece->iscale / roi_in->scale;
171 172 173 174 175 176 177 178 179 180 181
  const float sigma_r = 250.0f; // does not depend on scale
  const float sigma_s = 20.0f / scale;
  const float detail = -1.0f; // bilateral base layer

  dt_bilateral_t *b = dt_bilateral_init(roi_in->width, roi_in->height, sigma_s, sigma_r);
  dt_bilateral_splat(b, (float *)o);
  dt_bilateral_blur(b);
  dt_bilateral_slice(b, (float *)o, (float *)o, detail);
  dt_bilateral_free(b);

#ifdef _OPENMP
182
#pragma omp parallel for default(none) shared(d) schedule(static)
183
#endif
184
  for(int k = 0; k < roi_out->height; k++)
185
  {
186 187 188
    const float *in = ((float *)i) + (size_t)4 * k * roi_out->width;
    float *out = ((float *)o) + (size_t)4 * k * roi_out->width;
    for(int j = 0; j < roi_out->width; j++, in += 4, out += 4)
189
    {
190
      const float tt = envelope(in[0]);
191 192 193
      const float t = tt + (1.0f - tt) * (1.0f - d->highlights);
      out[0] = (1.0f - t) * in[0]
               + t * out[0] * (1.0f / 100.0f) * in[0]; // normalized filter * input brightness
194 195
    }
  }
196 197
}

198
#ifdef HAVE_OPENCL
199
int process_cl(struct dt_iop_module_t *self, dt_dev_pixelpipe_iop_t *piece, cl_mem dev_in, cl_mem dev_out,
200
               const dt_iop_roi_t *const roi_in, const dt_iop_roi_t *const roi_out)
201
{
202
  dt_iop_monochrome_data_t *d = (dt_iop_monochrome_data_t *)piece->data;
203 204 205 206 207 208 209
  dt_iop_monochrome_global_data_t *gd = (dt_iop_monochrome_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;
210
  const float sigma2 = (d->size * 128.0) * (d->size * 128.0f);
211

212
  // TODO: alloc new buffer, bilat filter, and go on with that
213
  const float scale = piece->iscale / roi_in->scale;
214 215 216 217 218
  const float sigma_r = 250.0f; // does not depend on scale
  const float sigma_s = 20.0f / scale;
  const float detail = -1.0f; // bilateral base layer

  cl_mem dev_tmp = NULL;
219
  dev_tmp = dt_opencl_alloc_device(devid, roi_in->width, roi_in->height, 4 * sizeof(float));
220 221 222 223

  dt_bilateral_cl_t *b = dt_bilateral_init_cl(devid, roi_in->width, roi_in->height, sigma_s, sigma_r);
  if(!b) goto error;

224
  size_t sizes[2] = { ROUNDUPWD(width), ROUNDUPHT(height) };
225
  dt_opencl_set_kernel_arg(devid, gd->kernel_monochrome_filter, 0, sizeof(cl_mem), &dev_in);
226
  dt_opencl_set_kernel_arg(devid, gd->kernel_monochrome_filter, 1, sizeof(cl_mem), &dev_out);
227 228 229 230 231 232 233 234
  dt_opencl_set_kernel_arg(devid, gd->kernel_monochrome_filter, 2, sizeof(int), &width);
  dt_opencl_set_kernel_arg(devid, gd->kernel_monochrome_filter, 3, sizeof(int), &height);
  dt_opencl_set_kernel_arg(devid, gd->kernel_monochrome_filter, 4, sizeof(float), &d->a);
  dt_opencl_set_kernel_arg(devid, gd->kernel_monochrome_filter, 5, sizeof(float), &d->b);
  dt_opencl_set_kernel_arg(devid, gd->kernel_monochrome_filter, 6, sizeof(float), &sigma2);
  err = dt_opencl_enqueue_kernel_2d(devid, gd->kernel_monochrome_filter, sizes);
  if(err != CL_SUCCESS) goto error;

235
  err = dt_bilateral_splat_cl(b, dev_out);
236
  if(err != CL_SUCCESS) goto error;
237
  err = dt_bilateral_blur_cl(b);
238
  if(err != CL_SUCCESS) goto error;
239
  err = dt_bilateral_slice_cl(b, dev_out, dev_tmp, detail);
240
  if(err != CL_SUCCESS) goto error;
241
  dt_bilateral_free_cl(b);
242
  b = NULL; // make sure we don't do double cleanup in case the next few lines err out
243

244
  dt_opencl_set_kernel_arg(devid, gd->kernel_monochrome, 0, sizeof(cl_mem), &dev_in);
245 246 247 248 249 250 251 252
  dt_opencl_set_kernel_arg(devid, gd->kernel_monochrome, 1, sizeof(cl_mem), &dev_tmp);
  dt_opencl_set_kernel_arg(devid, gd->kernel_monochrome, 2, sizeof(cl_mem), &dev_out);
  dt_opencl_set_kernel_arg(devid, gd->kernel_monochrome, 3, sizeof(int), &width);
  dt_opencl_set_kernel_arg(devid, gd->kernel_monochrome, 4, sizeof(int), &height);
  dt_opencl_set_kernel_arg(devid, gd->kernel_monochrome, 5, sizeof(float), &d->a);
  dt_opencl_set_kernel_arg(devid, gd->kernel_monochrome, 6, sizeof(float), &d->b);
  dt_opencl_set_kernel_arg(devid, gd->kernel_monochrome, 7, sizeof(float), &sigma2);
  dt_opencl_set_kernel_arg(devid, gd->kernel_monochrome, 8, sizeof(float), &d->highlights);
253 254 255
  err = dt_opencl_enqueue_kernel_2d(devid, gd->kernel_monochrome, sizes);
  if(err != CL_SUCCESS) goto error;

256
  dt_opencl_release_mem_object(dev_tmp);
257 258 259
  return TRUE;

error:
260
  dt_opencl_release_mem_object(dev_tmp);
261
  dt_bilateral_free_cl(b);
262 263 264 265 266
  dt_print(DT_DEBUG_OPENCL, "[opencl_monochrome] couldn't enqueue kernel! %d\n", err);
  return FALSE;
}
#endif

267 268 269
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)
270
{
271
  const float scale = piece->iscale / roi_in->scale;
272
  const float sigma_s = 20.0f / scale;
Tobias Ellinghaus's avatar
Tobias Ellinghaus committed
273
  const float sigma_r = 250.0f;
274 275 276 277 278

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

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

281 282 283
  tiling->factor = 3.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);
284
  tiling->overhead = 0;
285
  tiling->overlap = ceilf(4 * sigma_s);
286 287 288 289 290
  tiling->xalign = 1;
  tiling->yalign = 1;
  return;
}

291 292
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)
293 294 295 296 297 298 299 300 301 302 303 304 305 306
{
  dt_iop_monochrome_params_t *p = (dt_iop_monochrome_params_t *)p1;
  dt_iop_monochrome_data_t *d = (dt_iop_monochrome_data_t *)piece->data;
  d->a = p->a;
  d->b = p->b;
  d->size = p->size;
  d->highlights = p->highlights;

#ifdef HAVE_OPENCL
  piece->process_cl_ready = (piece->process_cl_ready && !(darktable.opencl->avoid_atomics));
#endif
}


307 308 309 310

void init_global(dt_iop_module_so_t *module)
{
  const int program = 2; // basic.cl from programs.conf
311 312
  dt_iop_monochrome_global_data_t *gd
      = (dt_iop_monochrome_global_data_t *)malloc(sizeof(dt_iop_monochrome_global_data_t));
313
  module->data = gd;
314
  gd->kernel_monochrome_filter = dt_opencl_create_kernel(program, "monochrome_filter");
315 316 317 318 319 320
  gd->kernel_monochrome = dt_opencl_create_kernel(program, "monochrome");
}

void cleanup_global(dt_iop_module_so_t *module)
{
  dt_iop_monochrome_global_data_t *gd = (dt_iop_monochrome_global_data_t *)module->data;
321
  dt_opencl_free_kernel(gd->kernel_monochrome_filter);
322 323 324 325 326
  dt_opencl_free_kernel(gd->kernel_monochrome);
  free(module->data);
  module->data = NULL;
}

327 328
void gui_update(struct dt_iop_module_t *self)
{
329
  dt_iop_monochrome_gui_data_t *g = (dt_iop_monochrome_gui_data_t *)self->gui_data;
330
  dt_iop_monochrome_params_t *p = (dt_iop_monochrome_params_t *)self->params;
331
  dt_bauhaus_slider_set(g->highlights, p->highlights);
332 333 334 335 336
  gtk_widget_queue_draw(self->widget);
}

void init(dt_iop_module_t *module)
{
337 338
  module->params = calloc(1, sizeof(dt_iop_monochrome_params_t));
  module->default_params = calloc(1, sizeof(dt_iop_monochrome_params_t));
339
  module->default_enabled = 0;
Heiko Bauke's avatar
Heiko Bauke committed
340
  module->priority = 632; // module order created by iop_dependencies.py, do not edit!
341 342
  module->params_size = sizeof(dt_iop_monochrome_params_t);
  module->gui_data = NULL;
343
  dt_iop_monochrome_params_t tmp = (dt_iop_monochrome_params_t){ 0., 0., 2., 0. };
344 345 346 347 348 349 350 351 352 353
  memcpy(module->params, &tmp, sizeof(dt_iop_monochrome_params_t));
  memcpy(module->default_params, &tmp, sizeof(dt_iop_monochrome_params_t));
}

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

354
void init_pipe(struct dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
355
{
356
  piece->data = calloc(1, sizeof(dt_iop_monochrome_data_t));
357 358 359
  self->commit_params(self, self->default_params, pipe, piece);
}

360
void cleanup_pipe(struct dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
361 362
{
  free(piece->data);
363
  piece->data = NULL;
364 365
}

366
static gboolean dt_iop_monochrome_draw(GtkWidget *widget, cairo_t *crf, gpointer user_data)
367 368 369
{
  dt_iop_module_t *self = (dt_iop_module_t *)user_data;
  dt_iop_monochrome_gui_data_t *g = (dt_iop_monochrome_gui_data_t *)self->gui_data;
370
  dt_iop_monochrome_params_t *p = (dt_iop_monochrome_params_t *)self->params;
371

372
  if(self->request_color_pick == DT_REQUEST_COLORPICK_MODULE)
373 374 375 376 377 378 379 380 381 382 383
  {
    float old_a = p->a, old_b = p->b, old_size = p->size;
    p->a = self->picked_color[1];
    p->b = self->picked_color[2];
    float da = self->picked_color_max[1] - self->picked_color_min[1];
    float db = self->picked_color_max[2] - self->picked_color_min[2];
    p->size = CLAMP((da + db)/128.0, .5, 3.0);
    if(old_a != p->a || old_b != p->b || old_size != p->size)
      dt_dev_add_history_item(darktable.develop, self, TRUE);
  }

384 385 386
  gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(g->colorpicker),
                               (self->request_color_pick == DT_REQUEST_COLORPICK_MODULE ? 1 : 0));

387
  const int inset = DT_COLORCORRECTION_INSET;
388 389 390
  GtkAllocation allocation;
  gtk_widget_get_allocation(widget, &allocation);
  int width = allocation.width, height = allocation.height;
391 392
  cairo_surface_t *cst = dt_cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height);
  cairo_t *cr = cairo_create(cst);
393
  // clear bg
394
  cairo_set_source_rgb(cr, .2, .2, .2);
395 396 397
  cairo_paint(cr);

  cairo_translate(cr, inset, inset);
398 399 400
  cairo_set_antialias(cr, CAIRO_ANTIALIAS_NONE);
  width -= 2 * inset;
  height -= 2 * inset;
401 402 403
  // clip region to inside:
  cairo_rectangle(cr, 0, 0, width, height);
  cairo_clip(cr);
404 405 406 407
  // flip y:
  cairo_translate(cr, 0, height);
  cairo_scale(cr, 1., -1.);
  const int cells = 8;
408 409
  for(int j = 0; j < cells; j++)
    for(int i = 0; i < cells; i++)
410
    {
411
      double rgb[3] = { 0.5, 0.5, 0.5 };
412 413 414 415
      cmsCIELab Lab;
      Lab.L = 53.390011;
      Lab.a = Lab.b = 0; // grey
      // dt_iop_sRGB_to_Lab(rgb, Lab, 0, 0, 1.0, 1, 1); // get grey in Lab
416 417 418 419
      Lab.a = PANEL_WIDTH * (i / (cells - 1.0) - .5);
      Lab.b = PANEL_WIDTH * (j / (cells - 1.0) - .5);
      const float f = color_filter(Lab.a, Lab.b, p->a, p->b, 40 * 40 * p->size * p->size);
      Lab.L *= f * f; // exaggerate filter a little
420
      cmsDoTransform(g->xform, &Lab, rgb, 1);
421 422 423 424
      cairo_set_source_rgb(cr, rgb[0], rgb[1], rgb[2]);
      cairo_rectangle(cr, width * i / (float)cells, height * j / (float)cells,
                      width / (float)cells - DT_PIXEL_APPLY_DPI(1),
                      height / (float)cells - DT_PIXEL_APPLY_DPI(1));
425 426
      cairo_fill(cr);
    }
427
  cairo_set_antialias(cr, CAIRO_ANTIALIAS_DEFAULT);
428
  cairo_set_source_rgb(cr, .7, .7, .7);
429
  cairo_set_line_width(cr, DT_PIXEL_APPLY_DPI(2.0));
430 431
  const float x = p->a * width / PANEL_WIDTH + width * .5f, y = p->b * height / PANEL_WIDTH + height * .5f;
  cairo_arc(cr, x, y, width * .22f * p->size, 0, 2.0 * M_PI);
432 433
  cairo_stroke(cr);

434
  if(g->dragging) dt_dev_add_history_item(darktable.develop, self, TRUE);
435

436 437 438 439
  cairo_destroy(cr);
  cairo_set_source_surface(crf, cst, 0, 0);
  cairo_paint(crf);
  cairo_surface_destroy(cst);
440 441 442
  return TRUE;
}

443
static gboolean dt_iop_monochrome_motion_notify(GtkWidget *widget, GdkEventMotion *event, gpointer user_data)
444 445 446 447 448 449 450
{
  dt_iop_module_t *self = (dt_iop_module_t *)user_data;
  dt_iop_monochrome_gui_data_t *g = (dt_iop_monochrome_gui_data_t *)self->gui_data;
  dt_iop_monochrome_params_t *p = (dt_iop_monochrome_params_t *)self->params;
  if(g->dragging)
  {
    const int inset = DT_COLORCORRECTION_INSET;
451 452
    GtkAllocation allocation;
    gtk_widget_get_allocation(widget, &allocation);
453
    int width = allocation.width - 2 * inset, height = allocation.height - 2 * inset;
454 455
    const float mouse_x = CLAMP(event->x - inset, 0, width);
    const float mouse_y = CLAMP(height - 1 - event->y + inset, 0, height);
456 457
    p->a = PANEL_WIDTH * (mouse_x - width * 0.5f) / (float)width;
    p->b = PANEL_WIDTH * (mouse_y - height * 0.5f) / (float)height;
458 459 460
    gtk_widget_queue_draw(self->widget);
  }
  gint x, y;
461
#if GTK_CHECK_VERSION(3, 20, 0)
462
  gdk_window_get_device_position(event->window,
johannes hanika's avatar
johannes hanika committed
463 464
      gdk_seat_get_pointer(gdk_display_get_default_seat(gtk_widget_get_display(widget))),
      &x, &y, 0);
465 466 467 468 469 470
#else
  gdk_window_get_device_position(event->window,
                                 gdk_device_manager_get_client_pointer(
                                     gdk_display_get_device_manager(gdk_window_get_display(event->window))),
                                 &x, &y, NULL);
#endif
471 472 473
  return TRUE;
}

474
static gboolean dt_iop_monochrome_button_press(GtkWidget *widget, GdkEventButton *event, gpointer user_data)
475
{
476 477 478 479 480
  if(event->button == 1)
  {
    dt_iop_module_t *self = (dt_iop_module_t *)user_data;
    dt_iop_monochrome_gui_data_t *g = (dt_iop_monochrome_gui_data_t *)self->gui_data;
    dt_iop_monochrome_params_t *p = (dt_iop_monochrome_params_t *)self->params;
481
    self->request_color_pick = DT_REQUEST_COLORPICK_OFF;
482 483 484 485 486 487 488 489 490 491 492
    if(event->type == GDK_2BUTTON_PRESS)
    {
      // reset
      dt_iop_monochrome_params_t *p0 = (dt_iop_monochrome_params_t *)self->default_params;
      p->a = p0->a;
      p->b = p0->b;
      p->size = p0->size;
    }
    else
    {
      const int inset = DT_COLORCORRECTION_INSET;
493 494
      GtkAllocation allocation;
      gtk_widget_get_allocation(widget, &allocation);
495
      int width = allocation.width - 2 * inset, height = allocation.height - 2 * inset;
496 497
      const float mouse_x = CLAMP(event->x - inset, 0, width);
      const float mouse_y = CLAMP(height - 1 - event->y + inset, 0, height);
498 499
      p->a = PANEL_WIDTH * (mouse_x - width * 0.5f) / (float)width;
      p->b = PANEL_WIDTH * (mouse_y - height * 0.5f) / (float)height;
500
      g->dragging = 1;
501
      g_object_set(G_OBJECT(widget), "has-tooltip", FALSE, (gchar *)0);
502
    }
503
    gtk_widget_queue_draw(self->widget);
504 505 506
    return TRUE;
  }
  return FALSE;
507 508
}

509
static gboolean dt_iop_monochrome_button_release(GtkWidget *widget, GdkEventButton *event, gpointer user_data)
510
{
511 512 513 514
  if(event->button == 1)
  {
    dt_iop_module_t *self = (dt_iop_module_t *)user_data;
    dt_iop_monochrome_gui_data_t *g = (dt_iop_monochrome_gui_data_t *)self->gui_data;
515
    self->request_color_pick = DT_REQUEST_COLORPICK_OFF;
516
    g->dragging = 0;
517
    g_object_set(G_OBJECT(widget), "has-tooltip", TRUE, (gchar *)0);
518 519 520
    return TRUE;
  }
  return FALSE;
521 522
}

523
static gboolean dt_iop_monochrome_leave_notify(GtkWidget *widget, GdkEventCrossing *event, gpointer user_data)
524 525 526 527 528 529 530 531
{
  dt_iop_module_t *self = (dt_iop_module_t *)user_data;
  dt_iop_monochrome_gui_data_t *g = (dt_iop_monochrome_gui_data_t *)self->gui_data;
  g->dragging = 0;
  gtk_widget_queue_draw(self->widget);
  return TRUE;
}

532
static gboolean dt_iop_monochrome_scrolled(GtkWidget *widget, GdkEventScroll *event, gpointer user_data)
533 534 535
{
  dt_iop_module_t *self = (dt_iop_module_t *)user_data;
  dt_iop_monochrome_params_t *p = (dt_iop_monochrome_params_t *)self->params;
536
  self->request_color_pick = DT_REQUEST_COLORPICK_OFF;
537

538 539
  gdouble delta_y;
  if(dt_gui_get_scroll_deltas(event, NULL, &delta_y))
540
  {
541
    p->size = CLAMP(p->size + delta_y * 0.1, 0.5f, 3.0f);
542 543 544
    dt_dev_add_history_item(darktable.develop, self, TRUE);
    gtk_widget_queue_draw(widget);
  }
545

546 547 548
  return TRUE;
}

549
static void highlights_callback(GtkWidget *w, gpointer user_data)
550 551 552
{
  dt_iop_module_t *self = (dt_iop_module_t *)user_data;
  dt_iop_monochrome_params_t *p = (dt_iop_monochrome_params_t *)self->params;
553
  self->request_color_pick = DT_REQUEST_COLORPICK_OFF;
554 555 556 557
  p->highlights = dt_bauhaus_slider_get(w);
  dt_dev_add_history_item(darktable.develop, self, TRUE);
}

558 559 560 561 562
static void picker_callback(GtkWidget *button, gpointer user_data)
{
  dt_iop_module_t *self = (dt_iop_module_t *)user_data;
  if(darktable.gui->reset) return;

563
  if(self->request_color_pick != DT_REQUEST_COLORPICK_MODULE)
564 565 566 567 568 569
    self->request_color_pick = DT_REQUEST_COLORPICK_MODULE;
  else
    self->request_color_pick = DT_REQUEST_COLORPICK_OFF;

  dt_iop_request_focus(self);

570
  if(self->request_color_pick == DT_REQUEST_COLORPICK_MODULE)
571 572
  {
    dt_lib_colorpicker_set_area(darktable.lib, 0.99);
573
    dt_dev_reprocess_all(self->dev);
574
  }
575 576 577 578 579 580
  else
    dt_control_queue_redraw();

  if(self->off) gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(self->off), 1);
}

581 582 583 584 585 586 587
void gui_init(struct dt_iop_module_t *self)
{
  self->gui_data = malloc(sizeof(dt_iop_monochrome_gui_data_t));
  dt_iop_monochrome_gui_data_t *g = (dt_iop_monochrome_gui_data_t *)self->gui_data;

  g->dragging = 0;

588
  self->widget = gtk_box_new(GTK_ORIENTATION_VERTICAL, DT_BAUHAUS_SPACE);
589
  g->area = GTK_DRAWING_AREA(dtgtk_drawing_area_new_with_aspect_ratio(1.0));
590
  gtk_box_pack_start(GTK_BOX(self->widget), GTK_WIDGET(g->area), TRUE, TRUE, 0);
591
  gtk_widget_set_tooltip_text(GTK_WIDGET(g->area), _("drag and scroll mouse wheel to adjust the virtual color filter"));
592 593 594

  gtk_widget_add_events(GTK_WIDGET(g->area), GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK
                                             | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK
595 596
                                             | GDK_LEAVE_NOTIFY_MASK | GDK_SCROLL_MASK
                                             | GDK_SMOOTH_SCROLL_MASK);
597
  g_signal_connect(G_OBJECT(g->area), "draw", G_CALLBACK(dt_iop_monochrome_draw), self);
598 599 600 601 602 603 604
  g_signal_connect(G_OBJECT(g->area), "button-press-event", G_CALLBACK(dt_iop_monochrome_button_press), self);
  g_signal_connect(G_OBJECT(g->area), "button-release-event", G_CALLBACK(dt_iop_monochrome_button_release),
                   self);
  g_signal_connect(G_OBJECT(g->area), "motion-notify-event", G_CALLBACK(dt_iop_monochrome_motion_notify),
                   self);
  g_signal_connect(G_OBJECT(g->area), "leave-notify-event", G_CALLBACK(dt_iop_monochrome_leave_notify), self);
  g_signal_connect(G_OBJECT(g->area), "scroll-event", G_CALLBACK(dt_iop_monochrome_scrolled), self);
605

606 607
  GtkWidget *box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, DT_BAUHAUS_SPACE);

608
  g->highlights = dt_bauhaus_slider_new_with_range(self, 0.0, 1.0, 0.01, 0.0, 2);
609
  self->request_color_pick = DT_REQUEST_COLORPICK_OFF;
610
  gtk_widget_set_tooltip_text(g->highlights, _("how much to keep highlights"));
611
  dt_bauhaus_widget_set_label(g->highlights, NULL, _("highlights"));
612
  gtk_box_pack_start(GTK_BOX(box), g->highlights, TRUE, TRUE, 0);
613
  g_signal_connect(G_OBJECT(g->highlights), "value-changed", G_CALLBACK(highlights_callback), self);
614 615 616 617 618 619 620

  g->colorpicker = dtgtk_togglebutton_new(dtgtk_cairo_paint_colorpicker, CPF_STYLE_FLAT | CPF_DO_NOT_USE_BORDER);
  gtk_widget_set_size_request(GTK_WIDGET(g->colorpicker), DT_PIXEL_APPLY_DPI(14), DT_PIXEL_APPLY_DPI(14));
  gtk_box_pack_end(GTK_BOX(box), GTK_WIDGET(g->colorpicker), FALSE, FALSE, 0);
  g_signal_connect(G_OBJECT(g->colorpicker), "toggled", G_CALLBACK(picker_callback), self);

  gtk_box_pack_end(GTK_BOX(self->widget), GTK_WIDGET(box), TRUE, TRUE, 0);
621

622 623 624
  cmsHPROFILE hsRGB = dt_colorspaces_get_profile(DT_COLORSPACE_SRGB, "", DT_PROFILE_DIRECTION_IN)->profile;
  cmsHPROFILE hLab = dt_colorspaces_get_profile(DT_COLORSPACE_LAB, "", DT_PROFILE_DIRECTION_ANY)->profile;
  g->xform = cmsCreateTransform(hLab, TYPE_Lab_DBL, hsRGB, TYPE_RGB_DBL, INTENT_PERCEPTUAL,
625
                                0); // cmsFLAGS_NOTPRECALC);
626 627 628 629
}

void gui_cleanup(struct dt_iop_module_t *self)
{
johannes hanika's avatar
johannes hanika committed
630 631
  dt_iop_monochrome_gui_data_t *g = (dt_iop_monochrome_gui_data_t *)self->gui_data;
  cmsDeleteTransform(g->xform);
632 633 634 635
  free(self->gui_data);
  self->gui_data = NULL;
}

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