Ansel 0.0
A darktable fork - bloat + design vision
Loading...
Searching...
No Matches
lowlight.c
Go to the documentation of this file.
1/*
2 This file is part of darktable,
3 Copyright (C) 2011 Brian Teague.
4 Copyright (C) 2011-2012 Henrik Andersson.
5 Copyright (C) 2011-2013, 2016 johannes hanika.
6 Copyright (C) 2011 Robert Bieber.
7 Copyright (C) 2011 Rostyslav Pidgornyi.
8 Copyright (C) 2011-2014, 2016-2017, 2019 Tobias Ellinghaus.
9 Copyright (C) 2012 Richard Wonka.
10 Copyright (C) 2012 Terry Jeffress.
11 Copyright (C) 2012, 2014, 2016, 2019 Ulrich Pegelow.
12 Copyright (C) 2013-2016 Roman Lebedev.
13 Copyright (C) 2013 Simon Spannagel.
14 Copyright (C) 2014 parafin.
15 Copyright (C) 2015 Pedro Côrte-Real.
16 Copyright (C) 2016 Asma.
17 Copyright (C) 2017-2018, 2021 Dan Torop.
18 Copyright (C) 2017 Heiko Bauke.
19 Copyright (C) 2018-2020, 2022-2023, 2025-2026 Aurélien PIERRE.
20 Copyright (C) 2018 Edgardo Hoszowski.
21 Copyright (C) 2018 Maurizio Paglia.
22 Copyright (C) 2018, 2020-2022 Pascal Obry.
23 Copyright (C) 2018 rawfiner.
24 Copyright (C) 2019 Andreas Schneider.
25 Copyright (C) 2019 emeikei.
26 Copyright (C) 2020 Aldric Renaudin.
27 Copyright (C) 2020-2022 Diederik Ter Rahe.
28 Copyright (C) 2020-2021 Ralf Brown.
29 Copyright (C) 2021 Chris Elston.
30 Copyright (C) 2022 Hanno Schwalm.
31 Copyright (C) 2022 Martin Bařinka.
32 Copyright (C) 2022 Philipp Lutz.
33
34 darktable is free software: you can redistribute it and/or modify
35 it under the terms of the GNU General Public License as published by
36 the Free Software Foundation, either version 3 of the License, or
37 (at your option) any later version.
38
39 darktable is distributed in the hope that it will be useful,
40 but WITHOUT ANY WARRANTY; without even the implied warranty of
41 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
42 GNU General Public License for more details.
43
44 You should have received a copy of the GNU General Public License
45 along with darktable. If not, see <http://www.gnu.org/licenses/>.
46*/
47#ifdef HAVE_CONFIG_H
48#include "config.h"
49#endif
50#include "bauhaus/bauhaus.h"
52#include "common/darktable.h"
53#include "common/debug.h"
54#include "common/opencl.h"
55#include "control/conf.h"
56#include "control/control.h"
57#include "develop/develop.h"
58#include "develop/imageop_gui.h"
59#include "dtgtk/drawingarea.h"
60
61#include "gui/draw.h"
62#include "gui/gtk.h"
63#include "gui/presets.h"
64#include "iop/iop_api.h"
65#include <inttypes.h>
66#include <math.h>
67#include <stdlib.h>
68#include <string.h>
69
71
72#define DT_IOP_LOWLIGHT_INSET DT_PIXEL_APPLY_DPI(5)
73#define DT_IOP_LOWLIGHT_RES 64
74#define DT_IOP_LOWLIGHT_BANDS 6
75#define DT_IOP_LOWLIGHT_LUT_RES 0x10000
76
78{
79 float blueness; // $MIN: 0.0 $MAX: 100.0 $DEFAULT: 0.0 $DESCRIPTION: "blue shift"
81 float transition_y[DT_IOP_LOWLIGHT_BANDS]; // $DEFAULT: 0.5
83
99
106
107const char *name()
108{
109 return _("lowlight vision");
110}
111
112const char **description(struct dt_iop_module_t *self)
113{
114 return dt_iop_set_description(self, _("simulate human night vision"),
115 _("creative"),
116 _("non-linear, Lab, display-referred"),
117 _("linear, XYZ"),
118 _("non-linear, Lab, display-referred"));
119}
120
125
127{
128 return IOP_GROUP_EFFECTS;
129}
130
132{
133 return IOP_CS_LAB;
134}
135
138{
139 default_input_format(self, pipe, piece, dsc);
140 dsc->channels = 4;
141 dsc->datatype = TYPE_FLOAT;
142}
143
144static inline __attribute__((always_inline)) float lookup(const float *lut, const float i)
145{
146 const int bin0 = MIN(0xffff, MAX(0, DT_IOP_LOWLIGHT_LUT_RES * i));
147 const int bin1 = MIN(0xffff, MAX(0, DT_IOP_LOWLIGHT_LUT_RES * i + 1));
148 const float f = DT_IOP_LOWLIGHT_LUT_RES * i - bin0;
149 return lut[bin1] * f + lut[bin0] * (1. - f);
150}
151
153int process(struct dt_iop_module_t *self, const dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece, const void *const i, void *const o)
154{
155 const dt_iop_roi_t *const roi_out = &piece->roi_out;
157 const int ch = 4;
158
159 // empiric coefficient
160 const float c = 0.5f;
161 const float threshold = 0.01f;
162
163 // scotopic white, blue saturated
164 dt_aligned_pixel_t Lab_sw = { 100.0f, 0, -d->blueness };
165 dt_aligned_pixel_t XYZ_sw;
166
167 dt_Lab_to_XYZ(Lab_sw, XYZ_sw);
169 for(size_t k = 0; k < (size_t)roi_out->width * roi_out->height; k++)
170 {
171 float *in = (float *)i + ch * k;
172 float *out = (float *)o + ch * k;
173 dt_aligned_pixel_t XYZ, XYZ_s;
174 float V;
175 float w;
176
177 dt_Lab_to_XYZ(in, XYZ);
178
179 // calculate scotopic luminance
180 if(XYZ[0] > threshold)
181 {
182 // normal flow
183 V = XYZ[1] * (1.33f * (1.0f + (XYZ[1] + XYZ[2]) / XYZ[0]) - 1.68f);
184 }
185 else
186 {
187 // low red flow, avoids "snow" on dark noisy areas
188 V = XYZ[1] * (1.33f * (1.0f + (XYZ[1] + XYZ[2]) / threshold) - 1.68f);
189 }
190
191 // scale using empiric coefficient and fit inside limits
192 V = fminf(1.0f, fmaxf(0.0f, c * V));
193
194 // blending coefficient from curve
195 w = lookup(d->lut, in[0] / 100.f);
196
197 XYZ_s[0] = V * XYZ_sw[0];
198 XYZ_s[1] = V * XYZ_sw[1];
199 XYZ_s[2] = V * XYZ_sw[2];
200
201 XYZ[0] = w * XYZ[0] + (1.0f - w) * XYZ_s[0];
202 XYZ[1] = w * XYZ[1] + (1.0f - w) * XYZ_s[1];
203 XYZ[2] = w * XYZ[2] + (1.0f - w) * XYZ_s[2];
204
206
207 out[3] = in[3];
208 }
209 return 0;
210}
211
214{
217 dt_draw_curve_set_point(d->curve, 0, p->transition_x[DT_IOP_LOWLIGHT_BANDS - 2] - 1.0, p->transition_y[0]);
218 for(int k = 0; k < DT_IOP_LOWLIGHT_BANDS; k++)
219 dt_draw_curve_set_point(d->curve, k + 1, p->transition_x[k], p->transition_y[k]);
220 dt_draw_curve_set_point(d->curve, DT_IOP_LOWLIGHT_BANDS + 1, p->transition_x[1] + 1.0,
221 p->transition_y[DT_IOP_LOWLIGHT_BANDS - 1]);
222 dt_draw_curve_calc_values(d->curve, 0.0, 1.0, DT_IOP_LOWLIGHT_LUT_RES, NULL, d->lut);
223 d->blueness = p->blueness;
224}
225
227{
230 piece->data = (void *)d;
231 piece->data_size = sizeof(dt_iop_lowlight_data_t);
232 d->curve = dt_draw_curve_new(0.0, 1.0, CATMULL_ROM);
233 (void)dt_draw_curve_add_point(d->curve, default_params->transition_x[DT_IOP_LOWLIGHT_BANDS - 2] - 1.0,
234 default_params->transition_y[DT_IOP_LOWLIGHT_BANDS - 2]);
235 for(int k = 0; k < DT_IOP_LOWLIGHT_BANDS; k++)
236 (void)dt_draw_curve_add_point(d->curve, default_params->transition_x[k], default_params->transition_y[k]);
237 (void)dt_draw_curve_add_point(d->curve, default_params->transition_x[1] + 1.0,
238 default_params->transition_y[1]);
239}
240
242{
243 // clean up everything again.
245 dt_draw_curve_destroy(d->curve);
246 dt_free_align(piece->data);
247 piece->data = NULL;
248}
249
250void gui_update(struct dt_iop_module_t *self)
251{
254 dt_bauhaus_slider_set(g->scale_blueness, p->blueness);
256 gtk_widget_queue_draw(self->widget);
257}
258
260{
261 dt_iop_default_init(module);
262
263 dt_iop_lowlight_params_t *d = module->default_params;
264
265 for(int k = 0; k < DT_IOP_LOWLIGHT_BANDS; k++) d->transition_x[k] = k / (DT_IOP_LOWLIGHT_BANDS - 1.0);
266}
267
269{
271
273
274 p.transition_x[0] = 0.000000;
275 p.transition_x[1] = 0.200000;
276 p.transition_x[2] = 0.400000;
277 p.transition_x[3] = 0.600000;
278 p.transition_x[4] = 0.800000;
279 p.transition_x[5] = 1.000000;
280
281 p.transition_y[0] = 1.000000;
282 p.transition_y[1] = 1.000000;
283 p.transition_y[2] = 1.000000;
284 p.transition_y[3] = 1.000000;
285 p.transition_y[4] = 1.000000;
286 p.transition_y[5] = 1.000000;
287
288 p.blueness = 0.0f;
289 dt_gui_presets_add_generic(_("daylight"), self->op,
290 self->version(), &p, sizeof(p), 1, DEVELOP_BLEND_CS_RGB_DISPLAY);
291
292 p.transition_x[0] = 0.000000;
293 p.transition_x[1] = 0.200000;
294 p.transition_x[2] = 0.400000;
295 p.transition_x[3] = 0.600000;
296 p.transition_x[4] = 0.800000;
297 p.transition_x[5] = 1.000000;
298
299 p.transition_y[0] = 0.600000;
300 p.transition_y[1] = 0.800000;
301 p.transition_y[2] = 0.950000;
302 p.transition_y[3] = 0.980000;
303 p.transition_y[4] = 1.000000;
304 p.transition_y[5] = 1.000000;
305
306 p.blueness = 30.0f;
307 dt_gui_presets_add_generic(_("indoor bright"), self->op,
308 self->version(), &p, sizeof(p), 1, DEVELOP_BLEND_CS_RGB_DISPLAY);
309
310 p.transition_x[0] = 0.000000;
311 p.transition_x[1] = 0.200000;
312 p.transition_x[2] = 0.400000;
313 p.transition_x[3] = 0.600000;
314 p.transition_x[4] = 0.800000;
315 p.transition_x[5] = 1.000000;
316
317 p.transition_y[0] = 0.300000;
318 p.transition_y[1] = 0.500000;
319 p.transition_y[2] = 0.700000;
320 p.transition_y[3] = 0.850000;
321 p.transition_y[4] = 0.970000;
322 p.transition_y[5] = 1.000000;
323
324 p.blueness = 30.0f;
325 dt_gui_presets_add_generic(_("indoor dim"), self->op,
326 self->version(), &p, sizeof(p), 1, DEVELOP_BLEND_CS_RGB_DISPLAY);
327
328 p.transition_x[0] = 0.000000;
329 p.transition_x[1] = 0.200000;
330 p.transition_x[2] = 0.400000;
331 p.transition_x[3] = 0.600000;
332 p.transition_x[4] = 0.800000;
333 p.transition_x[5] = 1.000000;
334
335 p.transition_y[0] = 0.050000;
336 p.transition_y[1] = 0.200000;
337 p.transition_y[2] = 0.400000;
338 p.transition_y[3] = 0.700000;
339 p.transition_y[4] = 0.920000;
340 p.transition_y[5] = 1.000000;
341
342 p.blueness = 40.0f;
343 dt_gui_presets_add_generic(_("indoor dark"), self->op,
344 self->version(), &p, sizeof(p), 1, DEVELOP_BLEND_CS_RGB_DISPLAY);
345
346 p.transition_x[0] = 0.000000;
347 p.transition_x[1] = 0.200000;
348 p.transition_x[2] = 0.400000;
349 p.transition_x[3] = 0.600000;
350 p.transition_x[4] = 0.800000;
351 p.transition_x[5] = 1.000000;
352
353 p.transition_y[0] = 0.070000;
354 p.transition_y[1] = 0.100000;
355 p.transition_y[2] = 0.180000;
356 p.transition_y[3] = 0.350000;
357 p.transition_y[4] = 0.750000;
358 p.transition_y[5] = 1.000000;
359
360 p.blueness = 50.0f;
361 dt_gui_presets_add_generic(_("twilight"), self->op,
362 self->version(), &p, sizeof(p), 1, DEVELOP_BLEND_CS_RGB_DISPLAY);
363
364 p.transition_x[0] = 0.000000;
365 p.transition_x[1] = 0.200000;
366 p.transition_x[2] = 0.400000;
367 p.transition_x[3] = 0.600000;
368 p.transition_x[4] = 0.800000;
369 p.transition_x[5] = 1.000000;
370
371 p.transition_y[0] = 0.000000;
372 p.transition_y[1] = 0.450000;
373 p.transition_y[2] = 0.750000;
374 p.transition_y[3] = 0.930000;
375 p.transition_y[4] = 0.990000;
376 p.transition_y[5] = 1.000000;
377
378 p.blueness = 30.0f;
379 dt_gui_presets_add_generic(_("night street lit"), self->op,
380 self->version(), &p, sizeof(p), 1, DEVELOP_BLEND_CS_RGB_DISPLAY);
381
382 p.transition_x[0] = 0.000000;
383 p.transition_x[1] = 0.200000;
384 p.transition_x[2] = 0.400000;
385 p.transition_x[3] = 0.600000;
386 p.transition_x[4] = 0.800000;
387 p.transition_x[5] = 1.000000;
388
389 p.transition_y[0] = 0.000000;
390 p.transition_y[1] = 0.150000;
391 p.transition_y[2] = 0.350000;
392 p.transition_y[3] = 0.800000;
393 p.transition_y[4] = 0.970000;
394 p.transition_y[5] = 1.000000;
395
396 p.blueness = 30.0f;
397 dt_gui_presets_add_generic(_("night street"), self->op,
398 self->version(), &p, sizeof(p), 1, DEVELOP_BLEND_CS_RGB_DISPLAY);
399
400 p.transition_x[0] = 0.000000;
401 p.transition_x[1] = 0.150000;
402 p.transition_x[2] = 0.400000;
403 p.transition_x[3] = 0.600000;
404 p.transition_x[4] = 0.800000;
405 p.transition_x[5] = 1.000000;
406
407 p.transition_y[0] = 0.000000;
408 p.transition_y[1] = 0.020000;
409 p.transition_y[2] = 0.050000;
410 p.transition_y[3] = 0.200000;
411 p.transition_y[4] = 0.550000;
412 p.transition_y[5] = 1.000000;
413
414 p.blueness = 40.0f;
415 dt_gui_presets_add_generic(_("night street dark"), self->op,
416 self->version(), &p, sizeof(p), 1, DEVELOP_BLEND_CS_RGB_DISPLAY);
417
418 p.transition_x[0] = 0.000000;
419 p.transition_x[1] = 0.200000;
420 p.transition_x[2] = 0.400000;
421 p.transition_x[3] = 0.600000;
422 p.transition_x[4] = 0.800000;
423 p.transition_x[5] = 1.000000;
424
425 p.transition_y[0] = 0.000000;
426 p.transition_y[1] = 0.000000;
427 p.transition_y[2] = 0.000000;
428 p.transition_y[3] = 0.000000;
429 p.transition_y[4] = 0.000000;
430 p.transition_y[5] = 0.000000;
431
432
433 p.blueness = 50.0f;
434 dt_gui_presets_add_generic(_("night"), self->op,
435 self->version(), &p, sizeof(p), 1, DEVELOP_BLEND_CS_RGB_DISPLAY);
436
438}
439
440// fills in new parameters based on mouse position (in 0,1)
441static void dt_iop_lowlight_get_params(dt_iop_lowlight_params_t *p, const double mouse_x,
442 const double mouse_y, const float rad)
443{
444 for(int k = 0; k < DT_IOP_LOWLIGHT_BANDS; k++)
445 {
446 const float f = expf(-(mouse_x - p->transition_x[k]) * (mouse_x - p->transition_x[k]) / (rad * rad));
447 p->transition_y[k] = (1 - f) * p->transition_y[k] + f * mouse_y;
448 }
449}
450
451static gboolean lowlight_draw(GtkWidget *widget, cairo_t *crf, gpointer user_data)
452{
453 dt_iop_module_t *self = (dt_iop_module_t *)user_data;
456
457 dt_draw_curve_set_point(c->transition_curve, 0, p.transition_x[DT_IOP_LOWLIGHT_BANDS - 2] - 1.0,
458 p.transition_y[0]);
459 for(int k = 0; k < DT_IOP_LOWLIGHT_BANDS; k++)
460 dt_draw_curve_set_point(c->transition_curve, k + 1, p.transition_x[k], p.transition_y[k]);
461 dt_draw_curve_set_point(c->transition_curve, DT_IOP_LOWLIGHT_BANDS + 1, p.transition_x[1] + 1.0,
462 p.transition_y[DT_IOP_LOWLIGHT_BANDS - 1]);
463
464 const int inset = DT_IOP_LOWLIGHT_INSET;
465 GtkAllocation allocation;
466 gtk_widget_get_allocation(widget, &allocation);
467 int width = allocation.width, height = allocation.height;
468 cairo_surface_t *cst = dt_cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height);
469 cairo_t *cr = cairo_create(cst);
470
471 cairo_set_source_rgb(cr, .2, .2, .2);
472 cairo_paint(cr);
473
474 cairo_translate(cr, inset, inset);
475 width -= 2 * inset;
476 height -= 2 * inset;
477
478 cairo_set_line_width(cr, DT_PIXEL_APPLY_DPI(1.0));
479 cairo_set_source_rgb(cr, .1, .1, .1);
480 cairo_rectangle(cr, 0, 0, width, height);
481 cairo_stroke(cr);
482
483 cairo_set_source_rgb(cr, .3, .3, .3);
484 cairo_rectangle(cr, 0, 0, width, height);
485 cairo_fill(cr);
486
487 // draw grid
488 cairo_set_line_width(cr, DT_PIXEL_APPLY_DPI(.4));
489 cairo_set_source_rgb(cr, .1, .1, .1);
490 dt_draw_grid(cr, 8, 0, 0, width, height);
491
492
493 if(c->mouse_y > 0 || c->dragging)
494 {
495 // draw min/max curves:
496 dt_iop_lowlight_get_params(&p, c->mouse_x, 1., c->mouse_radius);
497 dt_draw_curve_set_point(c->transition_curve, 0, p.transition_x[DT_IOP_LOWLIGHT_BANDS - 2] - 1.0,
498 p.transition_y[0]);
499 for(int k = 0; k < DT_IOP_LOWLIGHT_BANDS; k++)
500 dt_draw_curve_set_point(c->transition_curve, k + 1, p.transition_x[k], p.transition_y[k]);
501 dt_draw_curve_set_point(c->transition_curve, DT_IOP_LOWLIGHT_BANDS + 1, p.transition_x[1] + 1.0,
502 p.transition_y[DT_IOP_LOWLIGHT_BANDS - 1]);
503 dt_draw_curve_calc_values(c->transition_curve, 0.0, 1.0, DT_IOP_LOWLIGHT_RES, c->draw_min_xs,
504 c->draw_min_ys);
505
507 dt_iop_lowlight_get_params(&p, c->mouse_x, .0, c->mouse_radius);
508 dt_draw_curve_set_point(c->transition_curve, 0, p.transition_x[DT_IOP_LOWLIGHT_BANDS - 2] - 1.0,
509 p.transition_y[0]);
510 for(int k = 0; k < DT_IOP_LOWLIGHT_BANDS; k++)
511 dt_draw_curve_set_point(c->transition_curve, k + 1, p.transition_x[k], p.transition_y[k]);
512 dt_draw_curve_set_point(c->transition_curve, DT_IOP_LOWLIGHT_BANDS + 1, p.transition_x[1] + 1.0,
513 p.transition_y[DT_IOP_LOWLIGHT_BANDS - 1]);
514 dt_draw_curve_calc_values(c->transition_curve, 0.0, 1.0, DT_IOP_LOWLIGHT_RES, c->draw_max_xs,
515 c->draw_max_ys);
516 }
517
518 cairo_save(cr);
519
520 // draw x positions
521 cairo_set_source_rgb(cr, 0.6, 0.6, 0.6);
522 cairo_set_line_width(cr, DT_PIXEL_APPLY_DPI(1.));
523 const float arrw = DT_PIXEL_APPLY_DPI(7.0f);
524 for(int k = 0; k < DT_IOP_LOWLIGHT_BANDS; k++)
525 {
526 cairo_move_to(cr, width * p.transition_x[k], height + inset - DT_PIXEL_APPLY_DPI(1));
527 cairo_rel_line_to(cr, -arrw * .5f, 0);
528 cairo_rel_line_to(cr, arrw * .5f, -arrw);
529 cairo_rel_line_to(cr, arrw * .5f, arrw);
530 cairo_close_path(cr);
531 if(c->x_move == k)
532 cairo_fill(cr);
533 else
534 cairo_stroke(cr);
535 }
536
537 // draw selected cursor
538 cairo_translate(cr, 0, height);
539
540 // cairo_set_operator(cr, CAIRO_OPERATOR_ADD);
541 // cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
542 cairo_set_line_width(cr, DT_PIXEL_APPLY_DPI(2.));
543 cairo_set_source_rgba(cr, .7, .7, .7, 1.0);
544
546 dt_draw_curve_set_point(c->transition_curve, 0, p.transition_x[DT_IOP_LOWLIGHT_BANDS - 2] - 1.0,
547 p.transition_y[0]);
548 for(int k = 0; k < DT_IOP_LOWLIGHT_BANDS; k++)
549 dt_draw_curve_set_point(c->transition_curve, k + 1, p.transition_x[k], p.transition_y[k]);
550 dt_draw_curve_set_point(c->transition_curve, DT_IOP_LOWLIGHT_BANDS + 1, p.transition_x[1] + 1.0,
551 p.transition_y[DT_IOP_LOWLIGHT_BANDS - 1]);
552 dt_draw_curve_calc_values(c->transition_curve, 0.0, 1.0, DT_IOP_LOWLIGHT_RES, c->draw_xs, c->draw_ys);
553 cairo_move_to(cr, 0 * width / (float)(DT_IOP_LOWLIGHT_RES - 1), -height * c->draw_ys[0]);
554 for(int k = 1; k < DT_IOP_LOWLIGHT_RES; k++)
555 cairo_line_to(cr, k * width / (float)(DT_IOP_LOWLIGHT_RES - 1), -height * c->draw_ys[k]);
556 cairo_stroke(cr);
557
558 // draw dots on knots
559 cairo_set_source_rgb(cr, 0.7, 0.7, 0.7);
560 cairo_set_line_width(cr, DT_PIXEL_APPLY_DPI(1.));
561 for(int k = 0; k < DT_IOP_LOWLIGHT_BANDS; k++)
562 {
563 cairo_arc(cr, width * p.transition_x[k], -height * p.transition_y[k], DT_PIXEL_APPLY_DPI(3.0), 0.0,
564 2.0 * M_PI);
565 if(c->x_move == k)
566 cairo_fill(cr);
567 else
568 cairo_stroke(cr);
569 }
570
571 if(c->mouse_y > 0 || c->dragging)
572 {
573 // draw min/max, if selected
574 cairo_set_source_rgba(cr, .7, .7, .7, .6);
575 cairo_move_to(cr, 0, -height * c->draw_min_ys[0]);
576 for(int k = 1; k < DT_IOP_LOWLIGHT_RES; k++)
577 cairo_line_to(cr, k * width / (float)(DT_IOP_LOWLIGHT_RES - 1), -height * c->draw_min_ys[k]);
578 for(int k = DT_IOP_LOWLIGHT_RES - 1; k >= 0; k--)
579 cairo_line_to(cr, k * width / (float)(DT_IOP_LOWLIGHT_RES - 1), -height * c->draw_max_ys[k]);
580 cairo_close_path(cr);
581 cairo_fill(cr);
582 // draw mouse focus circle
583 cairo_set_source_rgba(cr, .9, .9, .9, .5);
584 const float pos = DT_IOP_LOWLIGHT_RES * c->mouse_x;
585 int k = (int)pos;
586 const float f = k - pos;
588 float ht = -height * (f * c->draw_ys[k] + (1 - f) * c->draw_ys[k + 1]);
589 cairo_arc(cr, c->mouse_x * width, ht, c->mouse_radius * width, 0, 2. * M_PI);
590 cairo_stroke(cr);
591 }
592
593 cairo_restore(cr);
594
595 cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
596
597 // draw labels:
598 PangoLayout *layout;
599 PangoRectangle ink;
600 PangoFontDescription *desc = pango_font_description_copy_static(darktable.bauhaus->pango_font_desc);
601 pango_font_description_set_weight(desc, PANGO_WEIGHT_BOLD);
602 pango_font_description_set_absolute_size(desc,(.06 * height) * PANGO_SCALE);
603 layout = pango_cairo_create_layout(cr);
604 pango_layout_set_font_description(layout, desc);
605 cairo_set_source_rgb(cr, .1, .1, .1);
606
607 pango_layout_set_text(layout, _("dark"), -1);
608 pango_layout_get_pixel_extents(layout, &ink, NULL);
609 cairo_move_to(cr, .02 * width - ink.y, .5 * (height + ink.width));
610 cairo_save(cr);
611 cairo_rotate(cr, -M_PI * .5f);
612 pango_cairo_show_layout(cr, layout);
613 cairo_restore(cr);
614
615 pango_layout_set_text(layout, _("bright"), -1);
616 pango_layout_get_pixel_extents(layout, &ink, NULL);
617 cairo_move_to(cr, .98 * width - ink.height, .5 * (height + ink.width));
618 cairo_save(cr);
619 cairo_rotate(cr, -M_PI * .5f);
620 pango_cairo_show_layout(cr, layout);
621 cairo_restore(cr);
622
623
624 pango_layout_set_text(layout, _("day vision"), -1);
625 pango_layout_get_pixel_extents(layout, &ink, NULL);
626 cairo_move_to(cr, .5 * (width - ink.width), .08 * height - ink.height);
627 pango_cairo_show_layout(cr, layout);
628
629 pango_layout_set_text(layout, _("night vision"), -1);
630 pango_layout_get_pixel_extents(layout, &ink, NULL);
631 cairo_move_to(cr, .5 * (width - ink.width), .97 * height - ink.height);
632 pango_cairo_show_layout(cr, layout);
633
634 pango_font_description_free(desc);
635 g_object_unref(layout);
636 cairo_destroy(cr);
637 cairo_set_source_surface(crf, cst, 0, 0);
638 cairo_paint(crf);
639 cairo_surface_destroy(cst);
640 return TRUE;
641}
642
643static gboolean lowlight_motion_notify(GtkWidget *widget, GdkEventMotion *event, gpointer user_data)
644{
645 dt_iop_module_t *self = (dt_iop_module_t *)user_data;
648 const int inset = DT_IOP_LOWLIGHT_INSET;
649 GtkAllocation allocation;
650 gtk_widget_get_allocation(widget, &allocation);
651 int height = allocation.height - 2 * inset, width = allocation.width - 2 * inset;
652 if(!c->dragging) c->mouse_x = CLAMP(event->x - inset, 0, width) / (float)width;
653 c->mouse_y = 1.0 - CLAMP(event->y - inset, 0, height) / (float)height;
654 if(c->dragging)
655 {
656 *p = c->drag_params;
657 if(c->x_move >= 0)
658 {
659 const float mx = CLAMP(event->x - inset, 0, width) / (float)width;
660 if(c->x_move > 0 && c->x_move < DT_IOP_LOWLIGHT_BANDS - 1)
661 {
662 const float minx = p->transition_x[c->x_move - 1] + 0.001f;
663 const float maxx = p->transition_x[c->x_move + 1] - 0.001f;
664 p->transition_x[c->x_move] = fminf(maxx, fmaxf(minx, mx));
665 }
666 }
667 else
668 {
669 dt_iop_lowlight_get_params(p, c->mouse_x, c->mouse_y + c->mouse_pick, c->mouse_radius);
670 }
671 gtk_widget_queue_draw(widget);
673 }
674 else if(event->y > height)
675 {
676 c->x_move = 0;
677 float dist = fabs(p->transition_x[0] - c->mouse_x);
678 for(int k = 1; k < DT_IOP_LOWLIGHT_BANDS; k++)
679 {
680 float d2 = fabs(p->transition_x[k] - c->mouse_x);
681 if(d2 < dist)
682 {
683 c->x_move = k;
684 dist = d2;
685 }
686 }
687 gtk_widget_queue_draw(widget);
688 }
689 else
690 {
691 c->x_move = -1;
692 gtk_widget_queue_draw(widget);
693 }
694 return TRUE;
695}
696
697static gboolean lowlight_button_press(GtkWidget *widget, GdkEventButton *event, gpointer user_data)
698{
699 dt_iop_module_t *self = (dt_iop_module_t *)user_data;
700 if(event->button == 1 && event->type == GDK_2BUTTON_PRESS)
701 {
702 // reset current curve
705 for(int k = 0; k < DT_IOP_LOWLIGHT_BANDS; k++)
706 {
707 p->transition_x[k] = d->transition_x[k];
708 p->transition_y[k] = d->transition_y[k];
709 }
711 gtk_widget_queue_draw(self->widget);
712 }
713 else if(event->button == 1)
714 {
716 c->drag_params = *(dt_iop_lowlight_params_t *)self->params;
717 const int inset = DT_IOP_LOWLIGHT_INSET;
718 GtkAllocation allocation;
719 gtk_widget_get_allocation(widget, &allocation);
720 int height = allocation.height - 2 * inset, width = allocation.width - 2 * inset;
721 c->mouse_pick
722 = dt_draw_curve_calc_value(c->transition_curve, CLAMP(event->x - inset, 0, width) / (float)width);
723 c->mouse_pick -= 1.0 - CLAMP(event->y - inset, 0, height) / (float)height;
724 c->dragging = 1;
725 return TRUE;
726 }
727 return FALSE;
728}
729
730static gboolean lowlight_button_release(GtkWidget *widget, GdkEventButton *event, gpointer user_data)
731{
732 if(event->button == 1)
733 {
734 dt_iop_module_t *self = (dt_iop_module_t *)user_data;
736 c->dragging = 0;
737 return TRUE;
738 }
739 return FALSE;
740}
741
742static gboolean lowlight_leave_notify(GtkWidget *widget, GdkEventCrossing *event, gpointer user_data)
743{
744 dt_iop_module_t *self = (dt_iop_module_t *)user_data;
746 if(!c->dragging) c->mouse_y = -1.0;
747 gtk_widget_queue_draw(widget);
748 return TRUE;
749}
750
751static gboolean lowlight_scrolled(GtkWidget *widget, GdkEventScroll *event, gpointer user_data)
752{
753 dt_iop_module_t *self = (dt_iop_module_t *)user_data;
755
756 int delta_y;
757 if(dt_gui_get_scroll_unit_deltas(event, NULL, &delta_y))
758 {
759 c->mouse_radius = CLAMP(c->mouse_radius * (1.0 + 0.1 * delta_y), 0.2 / DT_IOP_LOWLIGHT_BANDS, 1.0);
760 gtk_widget_queue_draw(widget);
761 }
762
763 return TRUE;
764}
765
766void gui_init(struct dt_iop_module_t *self)
767{
770
771 c->transition_curve = dt_draw_curve_new(0.0, 1.0, CATMULL_ROM);
772 (void)dt_draw_curve_add_point(c->transition_curve, p->transition_x[DT_IOP_LOWLIGHT_BANDS - 2] - 1.0,
773 p->transition_y[DT_IOP_LOWLIGHT_BANDS - 2]);
774 for(int k = 0; k < DT_IOP_LOWLIGHT_BANDS; k++)
775 (void)dt_draw_curve_add_point(c->transition_curve, p->transition_x[k], p->transition_y[k]);
776 (void)dt_draw_curve_add_point(c->transition_curve, p->transition_x[1] + 1.0, p->transition_y[1]);
777
778 c->mouse_x = c->mouse_y = c->mouse_pick = -1.0;
779 c->dragging = 0;
780 c->x_move = -1;
781 self->timeout_handle = 0;
782 c->mouse_radius = 1.0 / DT_IOP_LOWLIGHT_BANDS;
783
784 self->widget = gtk_box_new(GTK_ORIENTATION_VERTICAL, DT_GUI_BOX_SPACING);
785
786 c->area = GTK_DRAWING_AREA(gtk_drawing_area_new());
787 gtk_widget_set_hexpand(GTK_WIDGET(c->area), TRUE);
788 g_object_set_data(G_OBJECT(c->area), "iop-instance", self);
789 gtk_box_pack_start(GTK_BOX(self->widget),
790 dt_ui_resizable_drawing_area(GTK_WIDGET(c->area),
791 "plugins/darkroom/lowlight/graphheight", 280, 100),
792 FALSE, FALSE, 0);
793
794 gtk_widget_add_events(GTK_WIDGET(c->area), GDK_POINTER_MOTION_MASK | darktable.gui->scroll_mask
795 | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK
796 | GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK);
797 g_signal_connect(G_OBJECT(c->area), "draw", G_CALLBACK(lowlight_draw), self);
798 g_signal_connect(G_OBJECT(c->area), "button-press-event", G_CALLBACK(lowlight_button_press), self);
799 g_signal_connect(G_OBJECT(c->area), "button-release-event", G_CALLBACK(lowlight_button_release), self);
800 g_signal_connect(G_OBJECT(c->area), "motion-notify-event", G_CALLBACK(lowlight_motion_notify), self);
801 g_signal_connect(G_OBJECT(c->area), "leave-notify-event", G_CALLBACK(lowlight_leave_notify), self);
802 g_signal_connect(G_OBJECT(c->area), "scroll-event", G_CALLBACK(lowlight_scrolled), self);
803
804 c->scale_blueness = dt_bauhaus_slider_from_params(self, "blueness");
805 dt_bauhaus_slider_set_format(c->scale_blueness, "%");
806 gtk_widget_set_tooltip_text(c->scale_blueness, _("blueness in shadows"));
807}
808
809void gui_cleanup(struct dt_iop_module_t *self)
810{
812 dt_draw_curve_destroy(c->transition_curve);
814
816}
817
818// clang-format off
819// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
820// vim: shiftwidth=2 expandtab tabstop=2 cindent
821// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
822// clang-format on
static double dist(double x1, double y1, double x2, double y2)
Definition ashift_lsd.c:250
#define TRUE
Definition ashift_lsd.c:162
#define FALSE
Definition ashift_lsd.c:158
void dt_bauhaus_slider_set(GtkWidget *widget, float pos)
Definition bauhaus.c:3506
void dt_bauhaus_slider_set_format(GtkWidget *widget, const char *format)
Definition bauhaus.c:3598
int width
Definition bilateral.h:1
int height
Definition bilateral.h:1
@ DEVELOP_BLEND_CS_RGB_DISPLAY
Definition blend.h:59
static const dt_aligned_pixel_simd_t const dt_adaptation_t const float p
static float lookup(read_only image2d_t lut, const float x)
@ IOP_CS_LAB
dt_Lab_to_XYZ(Lab, XYZ)
const dt_aligned_pixel_t f
const float threshold
static dt_aligned_pixel_t XYZ
const dt_colormatrix_t dt_aligned_pixel_t out
dt_XYZ_to_Lab(XYZ, Lab)
typedef void((*dt_cache_allocate_t)(void *userdata, dt_cache_entry_t *entry))
#define CATMULL_ROM
Definition curve_tools.h:34
darktable_t darktable
Definition darktable.c:181
#define dt_free_align(ptr)
Definition darktable.h:481
static void * dt_calloc_align(size_t size)
Definition darktable.h:488
float dt_aligned_pixel_simd_t __attribute__((vector_size(16), aligned(16)))
Enable aggressive floating-point arithmetic optimizations, in denormals handling. Set through user pr...
Definition darktable.h:524
#define DT_MODULE_INTROSPECTION(MODVER, PARAMSTYPE)
Definition darktable.h:151
#define __DT_CLONE_TARGETS__
Definition darktable.h:367
#define __OMP_PARALLEL_FOR__(...)
Definition darktable.h:258
#define dt_database_start_transaction(db)
Definition database.h:77
#define dt_database_release_transaction(db)
Definition database.h:78
#define dt_dev_add_history_item(dev, module, enable, redraw)
void dt_iop_params_t
Definition dev_history.h:41
static void dt_draw_curve_calc_values(dt_draw_curve_t *c, const float min, const float max, const int res, float *x, float *y)
Definition draw.h:309
static void dt_draw_grid(cairo_t *cr, const int num, const int left, const int top, const int right, const int bottom)
Definition draw.h:143
static float dt_draw_curve_calc_value(dt_draw_curve_t *c, const float x)
Definition draw.h:345
static void dt_draw_curve_destroy(dt_draw_curve_t *c)
Definition draw.h:282
static void dt_draw_curve_set_point(dt_draw_curve_t *c, const int num, const float x, const float y)
Definition draw.h:288
static int dt_draw_curve_add_point(dt_draw_curve_t *c, const float x, const float y)
Definition draw.h:364
static dt_draw_curve_t * dt_draw_curve_new(const float min, const float max, unsigned int type)
Definition draw.h:266
void default_input_format(dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece, dt_iop_buffer_dsc_t *dsc)
Definition format.c:57
@ TYPE_FLOAT
Definition format.h:46
gboolean dt_gui_get_scroll_unit_deltas(const GdkEventScroll *event, int *delta_x, int *delta_y)
Definition gtk.c:219
GtkWidget * dt_ui_resizable_drawing_area(GtkWidget *area, char *config_str, int default_height, int min_height)
Make a self-drawing widget (typically a GtkDrawingArea graph or scope) vertically resizable.
Definition gtk.c:2836
static cairo_surface_t * dt_cairo_image_surface_create(cairo_format_t format, int width, int height)
Definition gtk.h:316
#define DT_GUI_BOX_SPACING
Definition gtk.h:109
#define DT_PIXEL_APPLY_DPI(value)
Definition gtk.h:90
void dt_gui_presets_add_generic(const char *name, dt_dev_operation_t op, const int32_t version, const void *params, const int32_t params_size, const int32_t enabled, const dt_develop_blend_colorspace_t blend_cst)
void dt_gui_throttle_cancel(gpointer source)
void dt_gui_throttle_queue(gpointer source, dt_gui_throttle_callback_t callback, gpointer user_data)
void dt_iop_throttled_history_update(gpointer data)
Definition imageop.c:3135
void dt_iop_default_init(dt_iop_module_t *module)
Definition imageop.c:316
const char ** dt_iop_set_description(dt_iop_module_t *module, const char *main_text, const char *purpose, const char *input, const char *process, const char *output)
Definition imageop.c:3141
#define IOP_GUI_FREE
Definition imageop.h:602
@ IOP_FLAGS_INCLUDE_IN_STYLES
Definition imageop.h:166
@ IOP_FLAGS_DEPRECATED
Definition imageop.h:168
@ IOP_FLAGS_SUPPORTS_BLENDING
Definition imageop.h:167
@ IOP_FLAGS_ALLOW_TILING
Definition imageop.h:169
@ IOP_GROUP_EFFECTS
Definition imageop.h:142
#define IOP_GUI_ALLOC(module)
Definition imageop.h:599
GtkWidget * dt_bauhaus_slider_from_params(dt_iop_module_t *self, const char *param)
Definition imageop_gui.c:77
const float *const lut
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)
Definition lowlight.c:212
void init(dt_iop_module_t *module)
Definition lowlight.c:259
const char ** description(struct dt_iop_module_t *self)
Definition lowlight.c:112
int default_group()
Definition lowlight.c:126
static gboolean lowlight_draw(GtkWidget *widget, cairo_t *crf, gpointer user_data)
Definition lowlight.c:451
static gboolean lowlight_scrolled(GtkWidget *widget, GdkEventScroll *event, gpointer user_data)
Definition lowlight.c:751
static gboolean lowlight_button_press(GtkWidget *widget, GdkEventButton *event, gpointer user_data)
Definition lowlight.c:697
static gboolean lowlight_motion_notify(GtkWidget *widget, GdkEventMotion *event, gpointer user_data)
Definition lowlight.c:643
#define DT_IOP_LOWLIGHT_BANDS
Definition lowlight.c:74
#define DT_IOP_LOWLIGHT_INSET
Definition lowlight.c:72
void init_pipe(struct dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
Definition lowlight.c:226
const char * name()
Definition lowlight.c:107
static gboolean lowlight_button_release(GtkWidget *widget, GdkEventButton *event, gpointer user_data)
Definition lowlight.c:730
#define DT_IOP_LOWLIGHT_RES
Definition lowlight.c:73
void gui_update(struct dt_iop_module_t *self)
Refresh GUI controls from current params and configuration.
Definition lowlight.c:250
void gui_init(struct dt_iop_module_t *self)
Definition lowlight.c:766
static gboolean lowlight_leave_notify(GtkWidget *widget, GdkEventCrossing *event, gpointer user_data)
Definition lowlight.c:742
int default_colorspace(dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece)
Definition lowlight.c:131
void input_format(dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece, dt_iop_buffer_dsc_t *dsc)
Definition lowlight.c:136
int flags()
Definition lowlight.c:121
void gui_cleanup(struct dt_iop_module_t *self)
Definition lowlight.c:809
void init_presets(dt_iop_module_so_t *self)
Definition lowlight.c:268
static void dt_iop_lowlight_get_params(dt_iop_lowlight_params_t *p, const double mouse_x, const double mouse_y, const float rad)
Definition lowlight.c:441
#define DT_IOP_LOWLIGHT_LUT_RES
Definition lowlight.c:75
__DT_CLONE_TARGETS__ int process(struct dt_iop_module_t *self, const dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece, const void *const i, void *const o)
Definition lowlight.c:153
void cleanup_pipe(struct dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
Definition lowlight.c:241
float *const restrict const size_t k
float *const restrict const size_t const size_t ch
#define M_PI
Definition math.h:45
float dt_aligned_pixel_t[4]
struct _GtkWidget GtkWidget
Definition splash.h:29
struct dt_gui_gtk_t * gui
Definition darktable.h:775
const struct dt_database_t * db
Definition darktable.h:779
struct dt_bauhaus_t * bauhaus
Definition darktable.h:778
struct dt_develop_t * develop
Definition darktable.h:770
PangoFontDescription * pango_font_desc
Definition bauhaus.h:274
struct dt_iop_module_t *void * data
gint scroll_mask
Definition gtk.h:224
unsigned int channels
Definition format.h:54
dt_iop_buffer_type_t datatype
Definition format.h:56
float lut[0x10000]
Definition lowlight.c:104
dt_draw_curve_t * curve
Definition lowlight.c:103
GtkWidget * scale_blueness
Definition lowlight.c:88
dt_iop_lowlight_params_t drag_params
Definition lowlight.c:92
dt_draw_curve_t * transition_curve
Definition lowlight.c:86
GtkDrawingArea * area
Definition lowlight.c:89
GModule *dt_dev_operation_t op
Definition imageop.h:230
dt_iop_params_t * default_params
Definition imageop.h:307
GtkWidget * widget
Definition imageop.h:337
dt_iop_gui_data_t * gui_data
Definition imageop.h:311
guint timeout_handle
Definition imageop.h:371
dt_iop_params_t * params
Definition imageop.h:307
Region of interest passed through the pixelpipe.
Definition imageop.h:72
#define MIN(a, b)
Definition thinplate.c:32
#define MAX(a, b)
Definition thinplate.c:29