Ansel 0.0
A darktable fork - bloat + design vision
Loading...
Searching...
No Matches
graduatednd.c
Go to the documentation of this file.
1/*
2 This file is part of darktable,
3 Copyright (C) 2010-2011 Bruce Guenter.
4 Copyright (C) 2010-2012 Henrik Andersson.
5 Copyright (C) 2010-2014, 2016, 2018 johannes hanika.
6 Copyright (C) 2010 Milan Knížek.
7 Copyright (C) 2011 Antony Dovgal.
8 Copyright (C) 2011 Brian Teague.
9 Copyright (C) 2011 Olivier Tribout.
10 Copyright (C) 2011 Robert Bieber.
11 Copyright (C) 2011 Rostyslav Pidgornyi.
12 Copyright (C) 2011-2014, 2016, 2019 Tobias Ellinghaus.
13 Copyright (C) 2012-2013, 2019-2020 Aldric Renaudin.
14 Copyright (C) 2012 Pascal de Bruijn.
15 Copyright (C) 2012 Richard Wonka.
16 Copyright (C) 2012-2014, 2017 Ulrich Pegelow.
17 Copyright (C) 2013 Dennis Gnad.
18 Copyright (C) 2013-2016 Roman Lebedev.
19 Copyright (C) 2013 Simon Spannagel.
20 Copyright (C) 2013 Thomas Pryds.
21 Copyright (C) 2015 Pedro Côrte-Real.
22 Copyright (C) 2017, 2019-2020 Heiko Bauke.
23 Copyright (C) 2018, 2020, 2022-2023, 2025-2026 Aurélien PIERRE.
24 Copyright (C) 2018-2019 Edgardo Hoszowski.
25 Copyright (C) 2018 Maurizio Paglia.
26 Copyright (C) 2018-2022 Pascal Obry.
27 Copyright (C) 2018 rawfiner.
28 Copyright (C) 2019 Andreas Schneider.
29 Copyright (C) 2019-2020, 2022 Diederik Ter Rahe.
30 Copyright (C) 2019 Diederik ter Rahe.
31 Copyright (C) 2020, 2022 Chris Elston.
32 Copyright (C) 2020 Nicolas Auffray.
33 Copyright (C) 2020-2021 Ralf Brown.
34 Copyright (C) 2021 Hubert Kowalski.
35 Copyright (C) 2022 Hanno Schwalm.
36 Copyright (C) 2022 Martin Bařinka.
37 Copyright (C) 2022 Philipp Lutz.
38 Copyright (C) 2023 Luca Zulberti.
39 Copyright (C) 2025, 2026 Guillaume Stutin.
40
41 darktable is free software: you can redistribute it and/or modify
42 it under the terms of the GNU General Public License as published by
43 the Free Software Foundation, either version 3 of the License, or
44 (at your option) any later version.
45
46 darktable is distributed in the hope that it will be useful,
47 but WITHOUT ANY WARRANTY; without even the implied warranty of
48 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
49 GNU General Public License for more details.
50
51 You should have received a copy of the GNU General Public License
52 along with darktable. If not, see <http://www.gnu.org/licenses/>.
53*/
54#ifdef HAVE_CONFIG_H
55#include "common/darktable.h"
56#include "config.h"
57#endif
58#include <assert.h>
59#include <math.h>
60#include <stdlib.h>
61#include <string.h>
62
63#include "bauhaus/bauhaus.h"
64#include "common/colorspaces.h"
65#include "common/debug.h"
66#include "common/math.h"
67#include "common/opencl.h"
68#include "control/control.h"
69#include "develop/develop.h"
70#include "develop/imageop.h"
72#include "develop/imageop_gui.h"
73#include "develop/tiling.h"
75
77#include "gui/draw.h"
78#include "gui/gtk.h"
79#include "gui/presets.h"
80#include "iop/iop_api.h"
81
83
85{
86 float density; // $MIN: -8.0 $MAX: 8.0 $DEFAULT: 1.0 $DESCRIPTION: "density" The density of filter 0-8 EV
87 float hardness; // $MIN: 0.0 $MAX: 100.0 $DEFAULT: 0.0 $DESCRIPTION: "hardness" 0% = soft and 100% = hard
88 float rotation; // $MIN: -180.0 $MAX: 180.0 $DEFAULT: 0.0 $DESCRIPTION: "rotation" 2*PI -180 - +180
89 float offset; // $DEFAULT: 50.0 $DESCRIPTION: "offset" centered, can be offsetted...
90 float hue; // $MIN: 0.0 $MAX: 1.0 $DEFAULT: 0.0 $DESCRIPTION: "hue"
91 float saturation; // $MIN: 0.0 $MAX: 1.0 $DEFAULT: 0.0 $DESCRIPTION: "saturation"
93
99
100
102{
104
105 dt_gui_presets_add_generic(_("neutral gray ND2 (soft)"), self->op, self->version(),
106 &(dt_iop_graduatednd_params_t){ 1, 0, 0, 50, 0, 0 },
108 dt_gui_presets_add_generic(_("neutral gray ND4 (soft)"), self->op, self->version(),
109 &(dt_iop_graduatednd_params_t){ 2, 0, 0, 50, 0, 0 },
111 dt_gui_presets_add_generic(_("neutral gray ND8 (soft)"), self->op, self->version(),
112 &(dt_iop_graduatednd_params_t){ 3, 0, 0, 50, 0, 0 },
114 dt_gui_presets_add_generic(_("neutral gray ND2 (hard)"), self->op, self->version(),
115 &(dt_iop_graduatednd_params_t){ 1, 75, 0, 50, 0, 0 },
117
118 dt_gui_presets_add_generic(_("neutral gray ND4 (hard)"), self->op, self->version(),
119 &(dt_iop_graduatednd_params_t){ 2, 75, 0, 50, 0, 0 },
121 dt_gui_presets_add_generic(_("neutral gray ND8 (hard)"), self->op, self->version(),
122 &(dt_iop_graduatednd_params_t){ 3, 75, 0, 50, 0, 0 },
124
125 dt_gui_presets_add_generic(_("orange ND2 (soft)"), self->op, self->version(),
126 &(dt_iop_graduatednd_params_t){ 1, 0, 0, 50, 0.102439, 0.8 },
128 dt_gui_presets_add_generic(_("yellow ND2 (soft)"), self->op, self->version(),
129 &(dt_iop_graduatednd_params_t){ 1, 0, 0, 50, 0.151220, 0.5 },
131
132 dt_gui_presets_add_generic(_("purple ND2 (soft)"), self->op, self->version(),
133 &(dt_iop_graduatednd_params_t){ 1, 0, 0, 50, 0.824390, 0.5 },
135
136 dt_gui_presets_add_generic(_("green ND2 (soft)"), self->op, self->version(),
137 &(dt_iop_graduatednd_params_t){ 1, 0, 0, 50, 0.302439, 0.5 },
139
140 dt_gui_presets_add_generic(_("red ND2 (soft)"), self->op, self->version(),
141 &(dt_iop_graduatednd_params_t){ 1, 0, 0, 50, 0, 0.5 },
143 dt_gui_presets_add_generic(_("blue ND2 (soft)"), self->op, self->version(),
144 &(dt_iop_graduatednd_params_t){ 1, 0, 0, 50, 0.663415, 0.5 },
146
147 dt_gui_presets_add_generic(_("brown ND4 (soft)"), self->op, self->version(),
148 &(dt_iop_graduatednd_params_t){ 2, 0, 0, 50, 0.082927, 0.25 },
150
152}
153
154typedef struct grad_point_t
155{
156 float x;
157 float y;
172
174{
175 float density; // The density of filter 0-8 EV
176 float hardness; // Default 0% = soft and 100% = hard
177 float rotation; // 2*PI -180 - +180
178 float offset; // Default 50%, centered, can be offsetted...
179 float color[4]; // RGB color of gradient
180 float color1[4]; // inverted color (1 - c)
182
183
184const char *name()
185{
186 return _("graduated density");
187}
188
189const char **description(struct dt_iop_module_t *self)
190{
191 return dt_iop_set_description(self, _("simulate an optical graduated neutral density filter"),
192 _("corrective and creative"),
193 _("linear or non-linear, RGB, scene-referred"),
194 _("non-linear, RGB"),
195 _("non-linear, RGB, display-referred"));
196}
197
203
205{
206 return IOP_GROUP_EFFECTS;
207}
208
210{
211 return IOP_CS_RGB;
212}
213
214static inline float f(const float t, const float c, const float x)
215{
216 return (t / (1.0f + powf(c, -x * 6.0f)) + (1.0f - t) * (x * .5f + .5f));
217}
218
219typedef struct dt_iop_vector_2d_t
220{
221 double x;
222 double y;
224
225// determine the distance between the segment [(ax,ay)(bx,by)] and the point (xc,yc)
227{
228 grad_point_t s = { b.x - a.x, b.y - a.y };
229 grad_point_t u = { c.x - a.x, c.y - a.y };
230 const float sn2 = s.x * s.x + s.y * s.y;
231 if(sn2 <= 0.0f) return u.x * u.x + u.y * u.y;
232
233 const float t = CLAMP((s.x * u.x + s.y * u.y) / sn2, 0.0f, 1.0f);
234 const float dx = u.x - t * s.x;
235 const float dy = u.y - t * s.y;
236 return dx * dx + dy * dy;
237}
238
245static void _draw_end_marker(cairo_t *cr, const grad_point_t endpoint, const grad_point_t opposite, const float zoom_scale,
246 const float normal_sign, const gboolean active)
247{
248 grad_point_t e_1 = { 0.0f, 0.0f };
249 grad_point_t e_2 = { 0.0f, 0.0f };
250 const float dx = opposite.x - endpoint.x;
251 const float dy = opposite.y - endpoint.y;
252 const float length = dt_fast_hypotf(dx, dy);
253 const float x = DT_PIXEL_APPLY_DPI_DPP(15.0f) / zoom_scale;
254 const float inv_len = 1.0f / length;
255 const float ux = dx * inv_len;
256 const float uy = dy * inv_len;
257 const float px = -uy;
258 const float py = ux;
259
260 // e_1 is at distance x from endpoint along [endpoint, opposite].
261 e_1.x = endpoint.x + ux * x;
262 e_1.y = endpoint.y + uy * x;
263 // e_2 is the midpoint of [endpoint, e_1], offset by x * normal_sign on the perpendicular.
264 const float mx = (endpoint.x + e_1.x) * 0.5f;
265 const float my = (endpoint.y + e_1.y) * 0.5f;
266 e_2.x = mx + px * (x * normal_sign);
267 e_2.y = my + py * (x * normal_sign);
268
269 cairo_move_to(cr, endpoint.x, endpoint.y);
270 cairo_line_to(cr, e_1.x, e_1.y);
271 cairo_line_to(cr, e_2.x, e_2.y);
272 cairo_close_path(cr);
273 cairo_set_line_width(cr, DT_PIXEL_APPLY_DPI(1.0f) / zoom_scale);
274
275 dt_draw_set_color_overlay(cr, TRUE, active ? 1.0f : 0.5f);
276 cairo_fill_preserve(cr);
277 dt_draw_set_color_overlay(cr, FALSE, active ? 1.0f : 0.5f);
278 cairo_stroke(cr);
279
280 dt_draw_node(cr, TRUE, active, FALSE, zoom_scale, endpoint.x, endpoint.y);
281}
282
283static int set_grad_from_points(struct dt_iop_module_t *self, const grad_point_t *a, const grad_point_t *b,
284 float *rotation, float *offset)
285{
286 // we want absolute preview positions
287 float pts[4] = { a->x, a->y, b->x, b->y };
291 pts[0] /= (float)piece->buf_out.width;
292 pts[2] /= (float)piece->buf_out.width;
293 pts[1] /= (float)piece->buf_out.height;
294 pts[3] /= (float)piece->buf_out.height;
295
296 // directly compute the line angle from segment AB and keep one representative modulo PI
297 const float eps = .0001f;
298 const float diff_x = pts[2] - pts[0];
299 const float diff_y = pts[3] - pts[1];
300 if(fabsf(diff_x) <= eps && fabsf(diff_y) <= eps) return 9;
301 float v = atan2f(diff_y, diff_x);
302 *rotation = -v * 180.0f / M_PI;
303
304 // and now we go for the offset (more easy)
305 const float sinv = sinf(v);
306 const float cosv = cosf(v);
307 const float ofs = (-2.0f * sinv * pts[0]) + sinv - cosv + 1.0f + (2.0f * cosv * pts[1]);
308
309 *offset = ofs * 50.0f;
310
311 return 1;
312}
313
315 const float rotation, const float offset)
316{
317 // we get the extremities of the line
318 const float v = (-rotation / 180) * M_PI;
319 const float sinv = sinf(v);
320 const float cosv = cosf(v);
321 const float eps = 1e-6f;
323
325 if(IS_NULL_PTR(piece)) return 0;
326 float wp = piece->buf_out.width, hp = piece->buf_out.height;
327
328 const float off = offset / 100.0f;
329
330 if(fabsf(sinv) <= eps) // horizontal: swap x ends and y offset sign depending on rotation direction
331 {
332 const int fwd = (cosv > 0.0f);
333 pts[0] = wp * (fwd ? 0.1f : 0.9f);
334 pts[2] = wp * (fwd ? 0.9f : 0.1f);
335 pts[1] = pts[3] = hp * (fwd ? off : (1.0f - off));
336 }
337 else if(fabsf(fabsf(sinv) - 1.0f) <= eps) // vertical: swap y ends and x offset sign depending on rotation direction
338 {
339 const int fwd = (sinv < 0.0f);
340 pts[0] = pts[2] = wp * (fwd ? off : (1.0f - off));
341 pts[1] = hp * (fwd ? 0.9f : 0.1f);
342 pts[3] = hp * (fwd ? 0.1f : 0.9f);
343 }
344 else
345 {
346 // otherwise we determine the extremities
347 float xx1 = (sinv - cosv + 1.0f - offset / 50.0f) * wp * 0.5f / sinv;
348 float xx2 = (sinv + cosv + 1.0f - offset / 50.0f) * wp * 0.5f / sinv;
349 float yy1 = 0.0f;
350 float yy2 = hp;
351 const float aa = hp / (xx2 - xx1);
352 const float bb = -xx1 * aa;
353
354 // clamp extremities to image width and recompute y from the line equation y = a*x + b
355 xx1 = CLAMP(xx1, 0.0f, wp);
356 yy1 = aa * xx1 + bb;
357 xx2 = CLAMP(xx2, 0.0f, wp);
358 yy2 = aa * xx2 + bb;
359
360 // inset extremities away from image border by 10%
361 const float dx = xx2 - xx1;
362 const float dy = yy2 - yy1;
363 xx1 += dx * 0.1f;
364 yy1 += dy * 0.1f;
365 xx2 -= dx * 0.1f;
366 yy2 -= dy * 0.1f;
367
368 // near rotation: ax < bx; far rotation: bx < ax — in both cases just pick which end goes first
369 const int first_is_xx1 = (rotation < 90.0f && rotation > -90.0f) ? (xx1 < xx2) : (xx1 > xx2);
370 if(first_is_xx1)
371 {
372 pts[0] = xx1;
373 pts[1] = yy1;
374 pts[2] = xx2;
375 pts[3] = yy2;
376 }
377 else
378 {
379 pts[0] = xx2;
380 pts[1] = yy2;
381 pts[2] = xx1;
382 pts[3] = yy1;
383 }
384 }
385 // now we want that points to take care of distort modules
386
388 return 0;
390 a->x = pts[0];
391 a->y = pts[1];
392 b->x = pts[2];
393 b->y = pts[3];
394 return 1;
395}
396
397static inline void update_saturation_slider_end_color(GtkWidget *slider, float hue)
398{
400 hsl2rgb(rgb, hue, 1.0, 0.5);
401 dt_bauhaus_slider_set_stop(slider, 1.0, rgb[0], rgb[1], rgb[2]);
402}
403
405{
408
409 // convert picker RGB 2 HSL
410 float H = .0f, S = .0f, L = .0f;
411 rgb2hsl(self->picked_color, &H, &S, &L);
412
413 if(fabsf(p->hue - H) < 0.0001f && fabsf(p->saturation - S) < 0.0001f)
414 {
415 // interrupt infinite loops
416 return;
417 }
418
419 p->hue = H;
420 p->saturation = S;
421
423 dt_bauhaus_slider_set(g->hue, p->hue);
424 dt_bauhaus_slider_set(g->saturation, p->saturation);
425 update_saturation_slider_end_color(g->saturation, p->hue);
427
429}
430
431void gui_reset(struct dt_iop_module_t *self)
432{
434}
435
436void gui_post_expose(struct dt_iop_module_t *self, cairo_t *cr, int32_t width, int32_t height,
437 int32_t pointerx, int32_t pointery)
438{
439 dt_develop_t *dev = self->dev;
442 if(IS_NULL_PTR(g) || IS_NULL_PTR(p)) return;
443
444 const float zoom_scale = dev->roi.scaling;
446
447 // we get the extremities of the line
448 if(g->define == 0)
449 {
450 if(!set_points_from_grad(self, &g->a, &g->b, p->rotation, p->offset))
451 return;
452 g->define = 1;
453 }
454
455 float line[4] = { g->a.x, g->a.y, g->b.x, g->b.y };
457 grad_point_t a = { line[0], line[1] };
458 grad_point_t b = { line[2], line[3] };
459
460 // the lines
461 cairo_set_line_cap(cr, CAIRO_LINE_CAP_ROUND);
462 if(g->selected == 3 || g->dragging == 3)
463 cairo_set_line_width(cr, DT_PIXEL_APPLY_DPI(5.0) / zoom_scale);
464 else
465 cairo_set_line_width(cr, DT_PIXEL_APPLY_DPI(3.0) / zoom_scale);
467
468 cairo_move_to(cr, a.x, a.y);
469 cairo_line_to(cr, b.x, b.y);
470 cairo_stroke(cr);
471
472 if(g->selected == 3 || g->dragging == 3)
473 cairo_set_line_width(cr, DT_PIXEL_APPLY_DPI(2.0) / zoom_scale);
474 else
475 cairo_set_line_width(cr, DT_PIXEL_APPLY_DPI(1.0) / zoom_scale);
477 cairo_move_to(cr, a.x, a.y);
478 cairo_line_to(cr, b.x, b.y);
479 cairo_stroke(cr);
480
481 // the extremities
482 _draw_end_marker(cr, a, b, zoom_scale, 1.0f, g->selected == 1 || g->dragging == 1);
483 _draw_end_marker(cr, b, a, zoom_scale, -1.0f, g->selected == 2 || g->dragging == 2);
484}
485
486int mouse_moved(struct dt_iop_module_t *self, double x, double y, double pressure, int which)
487{
489 float pzxpy[2] = { (float)x, (float)y };
491 float pzx = pzxpy[0];
492 float pzy = pzxpy[1];
493
494 // are we dragging something ?
495 if(g->dragging > 0)
496 {
497 if(g->dragging == 1)
498 {
499 // we are dragging a
500 g->a.x = pzx;
501 g->a.y = pzy;
502 }
503 else if(g->dragging == 2)
504 {
505 // we are dragging b
506 g->b.x = pzx;
507 g->b.y = pzy;
508 }
509 else if(g->dragging == 3)
510 {
511 // we are dragging the entire line
512 g->a.x += pzx - g->oldx;
513 g->b.x += pzx - g->oldx;
514 g->a.y += pzy - g->oldy;
515 g->b.y += pzy - g->oldy;
516 g->oldx = pzx;
517 g->oldy = pzy;
518 }
519 }
520 else
521 {
522 g->selected = 0;
523 float ext[2] = { DT_GUI_MOUSE_EFFECT_RADIUS, 0 };
525
526 const float ext2 = ext[0] * ext[0];
527
528 const grad_point_t pz = { pzx, pzy };
529 const float da_x = pz.x - g->a.x;
530 const float da_y = pz.y - g->a.y;
531 const float db_x = pz.x - g->b.x;
532 const float db_y = pz.y - g->b.y;
533
534 // are we near extermity ?
535 if(da_x * da_x + da_y * da_y < ext2)
536 {
537 g->selected = 1;
538 }
539 else if(db_x * db_x + db_y * db_y < ext2)
540 {
541 g->selected = 2;
542 }
543 else if(_dist_seg(g->a, g->b, pz) < ext2 * 0.5f)
544 g->selected = 3;
545 }
546
547 if(g->selected > 0 || g->dragging > 0)
549
550 return 1;
551}
552
553int button_pressed(struct dt_iop_module_t *self, double x, double y, double pressure, int which, int type,
554 uint32_t state)
555{
557 float pzxpy[2] = { (float)x, (float)y };
559 float pzx = pzxpy[0];
560 float pzy = pzxpy[1];
561
562 if(which == 3)
563 {
564 // creating a line with right click
565 g->dragging = 2;
566 g->a.x = pzx;
567 g->a.y = pzy;
568 g->b.x = pzx;
569 g->b.y = pzy;
570 g->oldx = pzx;
571 g->oldy = pzy;
572 return 1;
573 }
574 else if(g->selected > 0 && which == 1)
575 {
576 g->dragging = g->selected;
577 g->oldx = pzx;
578 g->oldy = pzy;
579 return 1;
580 }
581 g->dragging = 0;
582 return 0;
583}
584
585int button_released(struct dt_iop_module_t *self, double x, double y, int which, uint32_t state)
586{
589 if(g->dragging > 0)
590 {
591 float rotation = 0.0;
592 float offset = 0.0;
593 set_grad_from_points(self, &g->a, &g->b, &rotation, &offset);
594
595 // if this is a "line dragging, we reset extremities, to be sure they are not outside the image
596 if(g->dragging == 3)
597 {
598 // whole line dragging should not change rotation, so we should reuse
599 // old rotation to avoid rounding issues
600
601 rotation = p->rotation;
602 set_points_from_grad(self, &g->a, &g->b, rotation, offset);
603 }
605 dt_bauhaus_slider_set(g->rotation, rotation);
607 p->rotation = rotation;
608 p->offset = offset;
609 g->dragging = 0;
611 }
612
613 g->dragging = 0;
614 return 0;
615}
616
617int scrolled(dt_iop_module_t *self, double x, double y, int up, uint32_t state)
618{
621 if(dt_modifier_is(state, GDK_CONTROL_MASK))
622 {
623 float dens;
624 if(up)
625 dens = fminf(8.0, p->density + 0.1);
626 else
627 dens = fmaxf(-8.0, p->density - 0.1);
628 if(dens != p->density)
629 {
630 dt_bauhaus_slider_set(g->density, dens);
631 }
632 return 1;
633 }
634 if(dt_modifier_is(state, GDK_SHIFT_MASK))
635 {
636 float comp;
637 if(up)
638 comp = fminf(100.0, p->hardness + 1.0);
639 else
640 comp = fmaxf(0.0, p->hardness - 1.0);
641 if(comp != p->hardness)
642 {
643 dt_bauhaus_slider_set(g->hardness, comp);
644 }
645 return 1;
646 }
647 return 0;
648}
649
650__OMP_DECLARE_SIMD__(simdlen(4))
651static inline float density_times_length(const float dens, const float length)
652{
653// return (dens * CLIP(0.5f + length) / 8.0f);
654 return (dens * CLAMP(0.5f + length, 0.0f, 1.0f) / 8.0f);
655}
656
657__OMP_DECLARE_SIMD__(simdlen(4))
658static inline float compute_density(const float dens, const float length)
659{
660#if 1
661 // !!! approximation is ok only when highest density is 8
662 // for input x = (data->density * CLIP( 0.5+length ), calculate 2^x as (e^(ln2*x/8))^8
663 // use exp2f approximation to calculate e^(ln2*x/8)
664 // in worst case - density==8,CLIP(0.5-length) == 1.0 it gives 0.6% of error
665 const float t = DT_M_LN2f * density_times_length(dens,length);
666 const float d1 = t * t * 0.5f;
667 const float d2 = d1 * t * 0.333333333f;
668 const float d3 = d2 * t * 0.25f;
669 const float d = 1 + t + d1 + d2 + d3; /* taylor series for e^x till x^4 */
670 // printf("%d %d %f\n",y,x,d);
671 float density = d * d;
672 density = density * density;
673 density = density * density;
674#else
675 // use fair exp2f
676 const float density = exp2f(dens * CLIP(0.5f + length));
677#endif
678 return density;
679}
680
682int process(struct dt_iop_module_t *self, const dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece, const void *const ivoid,
683 void *const ovoid)
684{
685 const dt_iop_roi_t *const roi_in = &piece->roi_in;
686 const dt_iop_roi_t *const roi_out = &piece->roi_out;
687 const dt_iop_graduatednd_data_t *const data = (const dt_iop_graduatednd_data_t *const)piece->data;
688 const int ch = piece->dsc_in.channels;
689
690 const int ix = (roi_in->x);
691 const int iy = (roi_in->y);
692 const float iw = piece->buf_in.width * roi_out->scale;
693 const float ih = piece->buf_in.height * roi_out->scale;
694 const float hw = iw / 2.0f;
695 const float hh = ih / 2.0f;
696 const float hw_inv = 1.0f / hw;
697 const float hh_inv = 1.0f / hh;
698 const float v = (-data->rotation / 180) * M_PI;
699 const float sinv = sinf(v);
700 const float cosv = cosf(v);
701 const float filter_radie = sqrtf((hh * hh) + (hw * hw)) / hh;
702 const float offset = data->offset / 100.0f * 2;
703
704 const float filter_hardness = 1.0 / filter_radie / (1.0 - (0.5 + (data->hardness / 100.0) * 0.9 / 2.0)) * 0.5;
705
706 const int width = roi_out->width;
707 const int height = roi_out->height;
708 if(data->density > 0)
709 {
711 for(int y = 0; y < height; y++)
712 {
713 const size_t k = (size_t)width * y * ch;
714 const float *const restrict in = (float *)ivoid + k;
715 float *const restrict out = (float *)ovoid + k;
716
717 float length = (sinv * (-1.0 + ix * hw_inv) - cosv * (-1.0 + (iy + y) * hh_inv) - 1.0 + offset)
718 * filter_hardness;
719 const float length_inc = sinv * hw_inv * filter_hardness;
720
721 for(int x = 0; x < width; x++)
722 {
723 const float density = compute_density(data->density, length);
724 __OMP_SIMD__(aligned(in, out : 16))
725 for(int l = 0; l < 4; l++)
726 {
727 out[ch*x+l] = MAX(0.0f, (in[ch*x+l] / (data->color[l] + data->color1[l] * density)));
728 }
729 length += length_inc;
730 }
731 }
732 }
733 else
734 {
736 for(int y = 0; y < height; y++)
737 {
738 const size_t k = (size_t)width * y * ch;
739 const float *const restrict in = (float *)ivoid + k;
740 float *const restrict out = (float *)ovoid + k;
741
742 float length = (sinv * (-1.0f + ix * hw_inv) - cosv * (-1.0f + (iy + y) * hh_inv) - 1.0f + offset)
743 * filter_hardness;
744 const float length_inc = sinv * hw_inv * filter_hardness;
745
746 for(int x = 0; x < width; x++)
747 {
748 const float density = compute_density(-data->density, -length);
749 __OMP_SIMD__(aligned(in, out : 16))
750 for(int l = 0; l < 4; l++)
751 {
752 out[ch*x+l] = MAX(0.0f, (in[ch*x+l] * (data->color[l] + data->color1[l] * density)));
753 }
754 length += length_inc;
755 }
756 }
757 }
758
760 dt_iop_alpha_copy(ivoid, ovoid, roi_out->width, roi_out->height);
761 return 0;
762}
763
764#ifdef HAVE_OPENCL
765int process_cl(struct dt_iop_module_t *self, const dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece, cl_mem dev_in, cl_mem dev_out)
766{
767 const dt_iop_roi_t *const roi_in = &piece->roi_in;
768 const dt_iop_roi_t *const roi_out = &piece->roi_out;
771
772 cl_int err = -999;
773 const int devid = pipe->devid;
774 const int width = roi_in->width;
775 const int height = roi_in->height;
776
777 const int ix = (roi_in->x);
778 const int iy = (roi_in->y);
779 const float iw = piece->buf_in.width * roi_out->scale;
780 const float ih = piece->buf_in.height * roi_out->scale;
781 const float hw = iw / 2.0f;
782 const float hh = ih / 2.0f;
783 const float hw_inv = 1.0f / hw;
784 const float hh_inv = 1.0f / hh;
785 const float v = (-data->rotation / 180) * M_PI;
786 const float sinv = sinf(v);
787 const float cosv = cosf(v);
788 const float filter_radie = sqrtf((hh * hh) + (hw * hw)) / hh;
789 const float offset = data->offset / 100.0f * 2;
790 const float density = data->density;
791
792#if 1
793 const float filter_hardness = 1.0 / filter_radie
794 / (1.0 - (0.5 + (data->hardness / 100.0) * 0.9 / 2.0)) * 0.5;
795#else
796 const float hardness = data->hardness / 100.0f;
797 const float t = 1.0f - .8f / (.8f + hardness);
798 const float c = 1.0f + 1000.0f * powf(4.0, hardness);
799#endif
800
801 const float length_base = (sinv * (-1.0 + ix * hw_inv) - cosv * (-1.0 + iy * hh_inv) - 1.0 + offset)
802 * filter_hardness;
803 const float length_inc_y = -cosv * hh_inv * filter_hardness;
804 const float length_inc_x = sinv * hw_inv * filter_hardness;
805
806 size_t sizes[] = { ROUNDUPDWD(width, devid), ROUNDUPDHT(height, devid), 1 };
807
808 int kernel = density > 0 ? gd->kernel_graduatedndp : gd->kernel_graduatedndm;
809
810 dt_opencl_set_kernel_arg(devid, kernel, 0, sizeof(cl_mem), (void *)&dev_in);
811 dt_opencl_set_kernel_arg(devid, kernel, 1, sizeof(cl_mem), (void *)&dev_out);
812 dt_opencl_set_kernel_arg(devid, kernel, 2, sizeof(int), (void *)&width);
813 dt_opencl_set_kernel_arg(devid, kernel, 3, sizeof(int), (void *)&height);
814 dt_opencl_set_kernel_arg(devid, kernel, 4, 4 * sizeof(float), (void *)data->color);
815 dt_opencl_set_kernel_arg(devid, kernel, 5, sizeof(float), (void *)&density);
816 dt_opencl_set_kernel_arg(devid, kernel, 6, sizeof(float), (void *)&length_base);
817 dt_opencl_set_kernel_arg(devid, kernel, 7, sizeof(float), (void *)&length_inc_x);
818 dt_opencl_set_kernel_arg(devid, kernel, 8, sizeof(float), (void *)&length_inc_y);
819 err = dt_opencl_enqueue_kernel_2d(devid, kernel, sizes);
820 if(err != CL_SUCCESS) goto error;
821 return TRUE;
822
823error:
824 dt_print(DT_DEBUG_OPENCL, "[opencl_graduatednd] couldn't enqueue kernel! %d\n", err);
825 return FALSE;
826}
827#endif
828
830{
831 const int program = 8; // extended.cl, from programs.conf
834 module->data = gd;
835 gd->kernel_graduatedndp = dt_opencl_create_kernel(program, "graduatedndp");
836 gd->kernel_graduatedndm = dt_opencl_create_kernel(program, "graduatedndm");
837}
838
846
847void gui_changed(dt_iop_module_t *self, GtkWidget *w, void *previous)
848{
851 if(w == g->rotation)
852 {
853 set_points_from_grad(self, &g->a, &g->b, p->rotation, p->offset);
854 }
855 else if(w == g->hue)
856 {
857 update_saturation_slider_end_color(g->saturation, p->hue);
858 gtk_widget_queue_draw(g->saturation);
859 }
860}
861
864{
867
868 d->density = p->density;
869 d->hardness = p->hardness;
870 d->rotation = p->rotation;
871 d->offset = p->offset;
872
873 hsl2rgb(d->color, p->hue, p->saturation, 0.5);
874 d->color[3] = 0.0f;
875
876 if(d->density < 0)
877 for(int l = 0; l < 4; l++) d->color[l] = 1.0 - d->color[l];
878
879 for(int l = 0; l < 4; l++) d->color1[l] = 1.0 - d->color[l];
880}
881
883{
885 piece->data_size = sizeof(dt_iop_graduatednd_data_t);
886}
887
889{
890 dt_free_align(piece->data);
891 piece->data = NULL;
892}
893
904
905void gui_init(struct dt_iop_module_t *self)
906{
908
909 g->density = dt_bauhaus_slider_from_params(self, "density");
910 dt_bauhaus_slider_set_format(g->density, _(" EV"));
911 gtk_widget_set_tooltip_text(g->density, _("the density in EV for the filter"));
912
913 g->hardness = dt_bauhaus_slider_from_params(self, "hardness");
914 dt_bauhaus_slider_set_format(g->hardness, "%");
915 /* xgettext:no-c-format */
916 gtk_widget_set_tooltip_text(g->hardness, _("hardness of graduation:\n0% = soft, 100% = hard"));
917
918 g->rotation = dt_bauhaus_slider_from_params(self, "rotation");
919 dt_bauhaus_slider_set_format(g->rotation, "\302\260");
920 gtk_widget_set_tooltip_text(g->rotation, _("rotation of filter -180 to 180 degrees"));
921
924 dt_bauhaus_slider_set_factor(g->hue, 360.0f);
925 dt_bauhaus_slider_set_format(g->hue, "\302\260");
926 dt_bauhaus_slider_set_stop(g->hue, 0.0f, 1.0f, 0.0f, 0.0f);
927 dt_bauhaus_slider_set_stop(g->hue, 0.166f, 1.0f, 1.0f, 0.0f);
928 dt_bauhaus_slider_set_stop(g->hue, 0.322f, 0.0f, 1.0f, 0.0f);
929 dt_bauhaus_slider_set_stop(g->hue, 0.498f, 0.0f, 1.0f, 1.0f);
930 dt_bauhaus_slider_set_stop(g->hue, 0.664f, 0.0f, 0.0f, 1.0f);
931 dt_bauhaus_slider_set_stop(g->hue, 0.830f, 1.0f, 0.0f, 1.0f);
932 dt_bauhaus_slider_set_stop(g->hue, 1.0f, 1.0f, 0.0f, 0.0f);
933 gtk_widget_set_tooltip_text(g->hue, _("select the hue tone of filter"));
934
935 g->saturation = dt_bauhaus_slider_from_params(self, "saturation");
936 dt_bauhaus_slider_set_format(g->saturation, "%");
937 dt_bauhaus_slider_set_stop(g->saturation, 0.0f, 0.2f, 0.2f, 0.2f);
938 dt_bauhaus_slider_set_stop(g->saturation, 1.0f, 1.0f, 1.0f, 1.0f);
939 gtk_widget_set_tooltip_text(g->saturation, _("select the saturation of filter"));
940
941 g->selected = 0;
942 g->dragging = 0;
943 g->define = 0;
944}
945
946// clang-format off
947// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
948// vim: shiftwidth=2 expandtab tabstop=2 cindent
949// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
950// clang-format on
static void error(char *msg)
Definition ashift_lsd.c:202
#define TRUE
Definition ashift_lsd.c:162
#define FALSE
Definition ashift_lsd.c:158
void dt_bauhaus_slider_set_stop(GtkWidget *widget, float stop, float r, float g, float b)
Definition bauhaus.c:2372
void dt_bauhaus_slider_set_feedback(GtkWidget *widget, int feedback)
Definition bauhaus.c:3580
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
void dt_bauhaus_slider_set_factor(GtkWidget *widget, float factor)
Definition bauhaus.c:3611
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
@ IOP_CS_RGB
void dt_iop_color_picker_reset(dt_iop_module_t *module, gboolean keep)
GtkWidget * dt_color_picker_new(dt_iop_module_t *module, dt_iop_color_picker_kind_t kind, GtkWidget *w)
@ DT_COLOR_PICKER_POINT
void rgb2hsl(const dt_aligned_pixel_t rgb, float *h, float *s, float *l)
void hsl2rgb(dt_aligned_pixel_t rgb, float h, float s, float l)
static dt_aligned_pixel_t rgb
const dt_aligned_pixel_t f
const dt_colormatrix_t dt_aligned_pixel_t out
#define S(V, params)
int type
void dt_control_queue_redraw_center()
request redraw of center window. This redraws the center view within a gdk critical section to preven...
Definition control.c:861
darktable_t darktable
Definition darktable.c:181
void dt_print(dt_debug_thread_t thread, const char *msg,...)
Definition darktable.c:1542
#define dt_free_align(ptr)
Definition darktable.h:481
static void * dt_calloc_align(size_t size)
Definition darktable.h:488
#define __OMP_SIMD__(...)
Definition darktable.h:262
@ DT_DEBUG_OPENCL
Definition darktable.h:722
float dt_boundingbox_t[4]
Definition darktable.h:709
#define dt_free(ptr)
Definition darktable.h:456
#define DT_MODULE_INTROSPECTION(MODVER, PARAMSTYPE)
Definition darktable.h:151
#define __OMP_DECLARE_SIMD__(...)
Definition darktable.h:263
#define __DT_CLONE_TARGETS__
Definition darktable.h:367
#define __OMP_PARALLEL_FOR__(...)
Definition darktable.h:258
static gboolean dt_modifier_is(const GdkModifierType state, const GdkModifierType desired_modifier_mask)
Definition darktable.h:893
#define IS_NULL_PTR(p)
C is way too permissive with !=, == and if(var) checks, which can mean too many things depending on w...
Definition darktable.h:281
#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
void dt_dev_coordinates_image_abs_to_image_norm(dt_develop_t *dev, float *points, size_t num_points)
Definition develop.c:1075
dt_dev_pixelpipe_iop_t * dt_dev_distort_get_iop_pipe(struct dt_dev_pixelpipe_t *pipe, struct dt_iop_module_t *module)
Definition develop.c:1593
int dt_dev_distort_transform_plus(const dt_dev_pixelpipe_t *pipe, const double iop_order, const int transf_direction, float *points, size_t points_count)
Definition develop.c:1557
void dt_dev_coordinates_image_norm_to_preview_abs(dt_develop_t *dev, float *points, size_t num_points)
Definition develop.c:1144
gboolean dt_dev_rescale_roi(dt_develop_t *dev, cairo_t *cr, int32_t width, int32_t height)
Scale the ROI to fit within given width/height, centered.
Definition develop.c:1824
void dt_dev_coordinates_image_norm_to_image_abs(dt_develop_t *dev, float *points, size_t num_points)
Definition develop.c:1060
void dt_dev_coordinates_widget_to_image_norm(dt_develop_t *dev, float *points, size_t num_points)
Coordinate conversion helpers between widget, normalized image, and absolute image spaces.
Definition develop.c:1003
int dt_dev_distort_backtransform_plus(const dt_dev_pixelpipe_t *pipe, const double iop_order, const int transf_direction, float *points, size_t points_count)
Definition develop.c:1586
@ DT_DEV_PIXELPIPE_DISPLAY_MASK
Definition develop.h:118
@ DT_DEV_TRANSFORM_DIR_FORW_EXCL
Definition develop.h:104
#define H
Definition diffuse.c:613
static void dt_draw_set_color_overlay(cairo_t *cr, gboolean bright, double alpha)
Definition draw.h:106
static void dt_draw_node(cairo_t *cr, const gboolean square, const gboolean point_action, const gboolean selected, const float zoom_scale, const float x, const float y)
Draw an node point of a mask.
Definition draw.h:597
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)
const char ** description(struct dt_iop_module_t *self)
int default_group()
__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 ivoid, void *const ovoid)
static float compute_density(const float dens, const float length)
static int set_grad_from_points(struct dt_iop_module_t *self, const grad_point_t *a, const grad_point_t *b, float *rotation, float *offset)
void init_pipe(struct dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
const char * name()
void gui_reset(struct dt_iop_module_t *self)
int scrolled(dt_iop_module_t *self, double x, double y, int up, uint32_t state)
void gui_update(struct dt_iop_module_t *self)
Refresh GUI controls from current params and configuration.
void gui_init(struct dt_iop_module_t *self)
int button_pressed(struct dt_iop_module_t *self, double x, double y, double pressure, int which, int type, uint32_t state)
static int set_points_from_grad(struct dt_iop_module_t *self, grad_point_t *a, grad_point_t *b, const float rotation, const float offset)
void gui_changed(dt_iop_module_t *self, GtkWidget *w, void *previous)
int button_released(struct dt_iop_module_t *self, double x, double y, int which, uint32_t state)
void cleanup_global(dt_iop_module_so_t *module)
int default_colorspace(dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece)
int flags()
void gui_post_expose(struct dt_iop_module_t *self, cairo_t *cr, int32_t width, int32_t height, int32_t pointerx, int32_t pointery)
void init_presets(dt_iop_module_so_t *self)
static float density_times_length(const float dens, const float length)
void cleanup_pipe(struct dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
static void _draw_end_marker(cairo_t *cr, const grad_point_t endpoint, const grad_point_t opposite, const float zoom_scale, const float normal_sign, const gboolean active)
Draw one triangular endpoint marker on the graduated line.
void init_global(dt_iop_module_so_t *module)
static void update_saturation_slider_end_color(GtkWidget *slider, float hue)
int process_cl(struct dt_iop_module_t *self, const dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece, cl_mem dev_in, cl_mem dev_out)
int mouse_moved(struct dt_iop_module_t *self, double x, double y, double pressure, int which)
static float _dist_seg(grad_point_t a, grad_point_t b, grad_point_t c)
void color_picker_apply(dt_iop_module_t *self, GtkWidget *picker, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
#define DT_GUI_MOUSE_EFFECT_RADIUS
Definition gtk.h:70
#define DT_PIXEL_APPLY_DPI_DPP(value)
Definition gtk.h:91
#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)
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
@ IOP_FLAGS_INCLUDE_IN_STYLES
Definition imageop.h:166
@ IOP_FLAGS_SUPPORTS_BLENDING
Definition imageop.h:167
@ IOP_FLAGS_ALLOW_TILING
Definition imageop.h:169
@ IOP_FLAGS_TILING_FULL_ROI
Definition imageop.h:171
@ 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
void *const ovoid
static float kernel(const float *x, const float *y)
static const float x
const int t
const float v
float *const restrict const size_t k
float *const restrict const size_t const size_t ch
#define DT_M_LN2f
Definition math.h:55
#define CLIP(x)
Definition math.h:81
#define M_PI
Definition math.h:45
float dt_aligned_pixel_t[4]
int dt_opencl_enqueue_kernel_2d(const int dev, const int kernel, const size_t *sizes)
Definition opencl.c:2136
int dt_opencl_create_kernel(const int prog, const char *name)
Definition opencl.c:2030
void dt_opencl_free_kernel(const int kernel)
Definition opencl.c:2073
int dt_opencl_set_kernel_arg(const int dev, const int kernel, const int num, const size_t size, const void *arg)
Definition opencl.c:2127
#define ROUNDUPDHT(a, b)
Definition opencl.h:82
#define ROUNDUPDWD(a, b)
Definition opencl.h:81
#define eps
Definition rcd.c:81
struct _GtkWidget GtkWidget
Definition splash.h:29
const float uint32_t state[4]
struct dt_gui_gtk_t * gui
Definition darktable.h:775
const struct dt_database_t * db
Definition darktable.h:779
struct dt_develop_t * develop
Definition darktable.h:770
dt_iop_buffer_dsc_t dsc_in
struct dt_iop_module_t *void * data
struct dt_dev_pixelpipe_t * virtual_pipe
Definition develop.h:251
float scaling
Definition develop.h:193
struct dt_develop_t::@17 roi
int32_t reset
Definition gtk.h:172
unsigned int channels
Definition format.h:54
GModule *dt_dev_operation_t op
Definition imageop.h:230
dt_iop_global_data_t * data
Definition imageop.h:233
struct dt_develop_t * dev
Definition imageop.h:296
dt_iop_gui_data_t * gui_data
Definition imageop.h:311
dt_iop_global_data_t * global_data
Definition imageop.h:314
dt_aligned_pixel_t picked_color
Definition imageop.h:272
dt_iop_params_t * params
Definition imageop.h:307
Region of interest passed through the pixelpipe.
Definition imageop.h:72
double scale
Definition imageop.h:74
#define MAX(a, b)
Definition thinplate.c:29