Ansel 0.0
A darktable fork - bloat + design vision
Loading...
Searching...
No Matches
blend_gui.c
Go to the documentation of this file.
1/*
2 This file is part of darktable,
3 Copyright (C) 2012 johannes hanika.
4 Copyright (C) 2012 Richard Wonka.
5 Copyright (C) 2012-2014, 2016-2019 Tobias Ellinghaus.
6 Copyright (C) 2012-2015, 2017, 2019 Ulrich Pegelow.
7 Copyright (C) 2013, 2016, 2022 Aldric Renaudin.
8 Copyright (C) 2013 Dennis Gnad.
9 Copyright (C) 2013 parafin.
10 Copyright (C) 2013, 2018-2022 Pascal Obry.
11 Copyright (C) 2013-2016 Roman Lebedev.
12 Copyright (C) 2013 Thomas Pryds.
13 Copyright (C) 2017-2018, 2020 Heiko Bauke.
14 Copyright (C) 2017, 2019, 2021 luzpaz.
15 Copyright (C) 2018-2019 Edgardo Hoszowski.
16 Copyright (C) 2018 rawfiner.
17 Copyright (C) 2019, 2022-2023, 2025-2026 Aurélien PIERRE.
18 Copyright (C) 2019-2022 Diederik Ter Rahe.
19 Copyright (C) 2019 Florian Wernert.
20 Copyright (C) 2019 mepi0011.
21 Copyright (C) 2020-2022 Chris Elston.
22 Copyright (C) 2020-2021 Harold le Clément de Saint-Marcq.
23 Copyright (C) 2020-2021 Marco.
24 Copyright (C) 2020-2021 Ralf Brown.
25 Copyright (C) 2021 Hanno Schwalm.
26 Copyright (C) 2021 Hubert Kowalski.
27 Copyright (C) 2021 Marco Carrarini.
28 Copyright (C) 2021 Mark-64.
29 Copyright (C) 2021-2022 Nicolas Auffray.
30 Copyright (C) 2022 Martin Bařinka.
31 Copyright (C) 2022 Philipp Lutz.
32 Copyright (C) 2023 Alynx Zhou.
33 Copyright (C) 2023 Luca Zulberti.
34 Copyright (C) 2025-2026 Guillaume Stutin.
35
36 darktable is free software: you can redistribute it and/or modify
37 it under the terms of the GNU General Public License as published by
38 the Free Software Foundation, either version 3 of the License, or
39 (at your option) any later version.
40
41 darktable is distributed in the hope that it will be useful,
42 but WITHOUT ANY WARRANTY; without even the implied warranty of
43 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
44 GNU General Public License for more details.
45
46 You should have received a copy of the GNU General Public License
47 along with darktable. If not, see <http://www.gnu.org/licenses/>.
48*/
49#include "common/darktable.h"
50#include "develop/blend.h"
51#include "bauhaus/bauhaus.h"
52#include "common/debug.h"
53#include "common/dtpthread.h"
54#include "common/math.h"
55#include "common/opencl.h"
56#include "common/iop_profile.h"
57#include "control/conf.h"
58#include "control/control.h"
59#include "develop/develop.h"
60#include "develop/imageop.h"
61#include "develop/imageop_gui.h"
62#include "develop/masks.h"
63#include "develop/tiling.h"
64#include "dtgtk/button.h"
66
67#include "gui/actions/menu.h"
68#include "gui/gtk.h"
69#include "gui/presets.h"
70#include "libs/colorpicker.h"
71
72#include <assert.h>
73#include <gmodule.h>
74#include <stdlib.h>
75#include <string.h>
76#include <strings.h>
77
78#define NEUTRAL_GRAY 0.5
79#define BLEND_MASKMODE_CONF_KEY "plugins/darkroom/blending/mask_mode_tab"
80
82 = { { NC_("blendmode", "normal"), DEVELOP_BLEND_NORMAL2 },
83 { NC_("blendmode", "normal bounded"), DEVELOP_BLEND_BOUNDED },
84 { NC_("blendmode", "lighten"), DEVELOP_BLEND_LIGHTEN },
85 { NC_("blendmode", "darken"), DEVELOP_BLEND_DARKEN },
86 { NC_("blendmode", "multiply"), DEVELOP_BLEND_MULTIPLY },
87 { NC_("blendmode", "average"), DEVELOP_BLEND_AVERAGE },
88 { NC_("blendmode", "addition"), DEVELOP_BLEND_ADD },
89 { NC_("blendmode", "subtract"), DEVELOP_BLEND_SUBTRACT },
90 { NC_("blendmode", "difference"), DEVELOP_BLEND_DIFFERENCE2 },
91 { NC_("blendmode", "screen"), DEVELOP_BLEND_SCREEN },
92 { NC_("blendmode", "overlay"), DEVELOP_BLEND_OVERLAY },
93 { NC_("blendmode", "softlight"), DEVELOP_BLEND_SOFTLIGHT },
94 { NC_("blendmode", "hardlight"), DEVELOP_BLEND_HARDLIGHT },
95 { NC_("blendmode", "vividlight"), DEVELOP_BLEND_VIVIDLIGHT },
96 { NC_("blendmode", "linearlight"), DEVELOP_BLEND_LINEARLIGHT },
97 { NC_("blendmode", "pinlight"), DEVELOP_BLEND_PINLIGHT },
98 { NC_("blendmode", "lightness"), DEVELOP_BLEND_LIGHTNESS },
99 { NC_("blendmode", "chromaticity"), DEVELOP_BLEND_CHROMATICITY },
100 { NC_("blendmode", "hue"), DEVELOP_BLEND_HUE },
101 { NC_("blendmode", "color"), DEVELOP_BLEND_COLOR },
102 { NC_("blendmode", "coloradjustment"), DEVELOP_BLEND_COLORADJUST },
103 { NC_("blendmode", "Lab lightness"), DEVELOP_BLEND_LAB_LIGHTNESS },
104 { NC_("blendmode", "Lab color"), DEVELOP_BLEND_LAB_COLOR },
105 { NC_("blendmode", "Lab L-channel"), DEVELOP_BLEND_LAB_L },
106 { NC_("blendmode", "Lab a-channel"), DEVELOP_BLEND_LAB_A },
107 { NC_("blendmode", "Lab b-channel"), DEVELOP_BLEND_LAB_B },
108 { NC_("blendmode", "HSV value"), DEVELOP_BLEND_HSV_VALUE },
109 { NC_("blendmode", "HSV color"), DEVELOP_BLEND_HSV_COLOR },
110 { NC_("blendmode", "RGB red channel"), DEVELOP_BLEND_RGB_R },
111 { NC_("blendmode", "RGB green channel"), DEVELOP_BLEND_RGB_G },
112 { NC_("blendmode", "RGB blue channel"), DEVELOP_BLEND_RGB_B },
113 { NC_("blendmode", "divide"), DEVELOP_BLEND_DIVIDE },
114 { NC_("blendmode", "geometric mean"), DEVELOP_BLEND_GEOMETRIC_MEAN },
115 { NC_("blendmode", "harmonic mean"), DEVELOP_BLEND_HARMONIC_MEAN },
116
118 { NC_("blendmode", "difference (deprecated)"), DEVELOP_BLEND_DIFFERENCE },
119 { NC_("blendmode", "subtract inverse (deprecated)"), DEVELOP_BLEND_SUBTRACT_INVERSE },
120 { NC_("blendmode", "divide inverse (deprecated)"), DEVELOP_BLEND_DIVIDE_INVERSE },
121 { "", 0 } };
122
124 = { { NC_("blendoperation", "normal"), 0 },
125 { NC_("blendoperation", "reverse"), DEVELOP_BLEND_REVERSE },
126 { "", 0 } };
127
129 = { { N_("default"), DEVELOP_BLEND_CS_NONE },
130 { N_("RAW"), DEVELOP_BLEND_CS_RAW },
131 { N_("Lab"), DEVELOP_BLEND_CS_LAB },
132 { N_("RGB (display)"), DEVELOP_BLEND_CS_RGB_DISPLAY },
133 { N_("RGB (scene)"), DEVELOP_BLEND_CS_RGB_SCENE },
134 { "", 0 } };
135
137 = { { N_("None"), 0 },
138 { N_("Uniform"), 1 },
139 { N_("Parametric mask"), 2 },
140 { N_("Drawn mask"), 3 },
141 { N_("Drawn & parametric mask"), 4 },
142 { N_("Reuse an existing mask"), 5 },
143 { "", 0 } };
144
146 = { { N_("exclusive"), DEVELOP_COMBINE_NORM_EXCL },
147 { N_("inclusive"), DEVELOP_COMBINE_NORM_INCL },
148 { N_("exclusive & inverted"), DEVELOP_COMBINE_INV_EXCL },
149 { N_("inclusive & inverted"), DEVELOP_COMBINE_INV_INCL },
150 { "", 0 } };
151
153 = { { N_("output before blur"), DEVELOP_MASK_GUIDE_OUT_BEFORE_BLUR },
154 { N_("input before blur"), DEVELOP_MASK_GUIDE_IN_BEFORE_BLUR },
155 { N_("output after blur"), DEVELOP_MASK_GUIDE_OUT_AFTER_BLUR },
156 { N_("input after blur"), DEVELOP_MASK_GUIDE_IN_AFTER_BLUR },
157 { "", 0 } };
158
160 = { { N_("off"), DEVELOP_COMBINE_NORM },
161 { N_("on"), DEVELOP_COMBINE_INV },
162 { "", 0 } };
163
165 = { { 0.0f, { 0, 0, 0, 1.0 } },
166 { 0.125f, { NEUTRAL_GRAY / 8, NEUTRAL_GRAY / 8, NEUTRAL_GRAY / 8, 1.0 } },
167 { 0.25f, { NEUTRAL_GRAY / 4, NEUTRAL_GRAY / 4, NEUTRAL_GRAY / 4, 1.0 } },
168 { 0.5f, { NEUTRAL_GRAY / 2, NEUTRAL_GRAY / 2, NEUTRAL_GRAY / 2, 1.0 } },
169 { 1.0f, { NEUTRAL_GRAY, NEUTRAL_GRAY, NEUTRAL_GRAY, 1.0 } } };
170
171// The values for "a" are generated in the following way:
172// Lab (with L=[90 to 68], b=0, and a=[-56 to 56] -> sRGB (D65 linear) -> normalize with MAX(R,G,B) = 0.75
174 { 0.000f, { 0.0112790f, 0.7500000f, 0.5609999f, 1.0f } },
175 { 0.250f, { 0.2888855f, 0.7500000f, 0.6318934f, 1.0f } },
176 { 0.375f, { 0.4872486f, 0.7500000f, 0.6825501f, 1.0f } },
177 { 0.500f, { 0.7500000f, 0.7499399f, 0.7496052f, 1.0f } },
178 { 0.625f, { 0.7500000f, 0.5054633f, 0.5676756f, 1.0f } },
179 { 0.750f, { 0.7500000f, 0.3423850f, 0.4463195f, 1.0f } },
180 { 1.000f, { 0.7500000f, 0.1399815f, 0.2956989f, 1.0f } },
181};
182
183// The values for "b" are generated in the following way:
184// Lab (with L=[58 to 62], a=0, and b=[-65 to 65] -> sRGB (D65 linear) -> normalize with MAX(R,G,B) = 0.75
186 { 0.000f, { 0.0162050f, 0.1968228f, 0.7500000f, 1.0f } },
187 { 0.250f, { 0.2027354f, 0.3168822f, 0.7500000f, 1.0f } },
188 { 0.375f, { 0.3645722f, 0.4210476f, 0.7500000f, 1.0f } },
189 { 0.500f, { 0.6167146f, 0.5833379f, 0.7500000f, 1.0f } },
190 { 0.625f, { 0.7500000f, 0.6172369f, 0.5412091f, 1.0f } },
191 { 0.750f, { 0.7500000f, 0.5590797f, 0.3071980f, 1.0f } },
192 { 1.000f, { 0.7500000f, 0.4963975f, 0.0549797f, 1.0f } },
193};
194
196 = { { 0.0f, { 0, 0, 0, 1.0 } },
197 { 0.125f, { NEUTRAL_GRAY / 8, NEUTRAL_GRAY / 8, NEUTRAL_GRAY / 8, 1.0 } },
198 { 0.25f, { NEUTRAL_GRAY / 4, NEUTRAL_GRAY / 4, NEUTRAL_GRAY / 4, 1.0 } },
199 { 0.5f, { NEUTRAL_GRAY / 2, NEUTRAL_GRAY / 2, NEUTRAL_GRAY / 2, 1.0 } },
200 { 1.0f, { NEUTRAL_GRAY, NEUTRAL_GRAY, NEUTRAL_GRAY, 1.0 } } };
201
203 { 0.000f, { 0.0000000f, 0.0000000f, 0.0000000f, 1.0f } },
204 { 0.125f, { 0.0937500f, 0.0000000f, 0.0000000f, 1.0f } },
205 { 0.250f, { 0.1875000f, 0.0000000f, 0.0000000f, 1.0f } },
206 { 0.500f, { 0.3750000f, 0.0000000f, 0.0000000f, 1.0f } },
207 { 1.000f, { 0.7500000f, 0.0000000f, 0.0000000f, 1.0f } }
208};
209
211 { 0.000f, { 0.0000000f, 0.0000000f, 0.0000000f, 1.0f } },
212 { 0.125f, { 0.0000000f, 0.0937500f, 0.0000000f, 1.0f } },
213 { 0.250f, { 0.0000000f, 0.1875000f, 0.0000000f, 1.0f } },
214 { 0.500f, { 0.0000000f, 0.3750000f, 0.0000000f, 1.0f } },
215 { 1.000f, { 0.0000000f, 0.7500000f, 0.0000000f, 1.0f } }
216};
217
219 { 0.000f, { 0.0000000f, 0.0000000f, 0.0000000f, 1.0f } },
220 { 0.125f, { 0.0000000f, 0.0000000f, 0.0937500f, 1.0f } },
221 { 0.250f, { 0.0000000f, 0.0000000f, 0.1875000f, 1.0f } },
222 { 0.500f, { 0.0000000f, 0.0000000f, 0.3750000f, 1.0f } },
223 { 1.000f, { 0.0000000f, 0.0000000f, 0.7500000f, 1.0f } }
224};
225
226// The chroma values are displayed in a gradient from {0.5,0.5,0.5} to {0.5,0.0,0.5} (pink)
228 { 0.000f, { 0.5000000f, 0.5000000f, 0.5000000f, 1.0f } },
229 { 0.125f, { 0.5000000f, 0.4375000f, 0.5000000f, 1.0f } },
230 { 0.250f, { 0.5000000f, 0.3750000f, 0.5000000f, 1.0f } },
231 { 0.500f, { 0.5000000f, 0.2500000f, 0.5000000f, 1.0f } },
232 { 1.000f, { 0.5000000f, 0.0000000f, 0.5000000f, 1.0f } }
233};
234
235// The hue values for LCh are generated in the following way:
236// LCh (with L=65 and C=37) -> sRGB (D65 linear) -> normalize with MAX(R,G,B) = 0.75
237// Please keep in sync with the display in the gamma module
239 { 0.000f, { 0.7500000f, 0.2200405f, 0.4480174f, 1.0f } },
240 { 0.104f, { 0.7500000f, 0.2475123f, 0.2488547f, 1.0f } },
241 { 0.200f, { 0.7500000f, 0.3921083f, 0.2017670f, 1.0f } },
242 { 0.295f, { 0.7500000f, 0.7440329f, 0.3011876f, 1.0f } },
243 { 0.377f, { 0.3813996f, 0.7500000f, 0.3799668f, 1.0f } },
244 { 0.503f, { 0.0747526f, 0.7500000f, 0.7489037f, 1.0f } },
245 { 0.650f, { 0.0282981f, 0.3736209f, 0.7500000f, 1.0f } },
246 { 0.803f, { 0.2583821f, 0.2591069f, 0.7500000f, 1.0f } },
247 { 0.928f, { 0.7500000f, 0.2788102f, 0.7492077f, 1.0f } },
248 { 1.000f, { 0.7500000f, 0.2200405f, 0.4480174f, 1.0f } },
249};
250
251// The hue values for HSL are generated in the following way:
252// HSL (with S=0.5 and L=0.5) -> any RGB(linear) -> (normalize with MAX(R,G,B) = 0.75)
253// Please keep in sync with the display in the gamma module
255 { 0.000f, { 0.7500000f, 0.2500000f, 0.2500000f, 1.0f } },
256 { 0.167f, { 0.7500000f, 0.7500000f, 0.2500000f, 1.0f } },
257 { 0.333f, { 0.2500000f, 0.7500000f, 0.2500000f, 1.0f } },
258 { 0.500f, { 0.2500000f, 0.7500000f, 0.7500000f, 1.0f } },
259 { 0.667f, { 0.2500000f, 0.2500000f, 0.7500000f, 1.0f } },
260 { 0.833f, { 0.7500000f, 0.2500000f, 0.7500000f, 1.0f } },
261 { 1.000f, { 0.7500000f, 0.2500000f, 0.2500000f, 1.0f } },
262};
263
264// The hue values for JzCzhz are generated in the following way:
265// JzCzhz (with Jz=0.011 and Cz=0.01) -> sRGB(D65 linear) -> normalize with MAX(R,G,B) = 0.75
266// Please keep in sync with the display in the gamma module
268 { 0.000f, { 0.7500000f, 0.1946971f, 0.3697612f, 1.0f } },
269 { 0.082f, { 0.7500000f, 0.2278141f, 0.2291548f, 1.0f } },
270 { 0.150f, { 0.7500000f, 0.3132381f, 0.1653960f, 1.0f } },
271 { 0.275f, { 0.7483232f, 0.7500000f, 0.1939316f, 1.0f } },
272 { 0.378f, { 0.2642865f, 0.7500000f, 0.2642768f, 1.0f } },
273 { 0.570f, { 0.0233180f, 0.7493543f, 0.7500000f, 1.0f } },
274 { 0.650f, { 0.1119025f, 0.5116763f, 0.7500000f, 1.0f } },
275 { 0.762f, { 0.3331225f, 0.3337235f, 0.7500000f, 1.0f } },
276 { 0.883f, { 0.7464700f, 0.2754816f, 0.7500000f, 1.0f } },
277 { 1.000f, { 0.7500000f, 0.1946971f, 0.3697612f, 1.0f } },
278};
279
298
299static void _blendop_blendif_update_tab(dt_iop_module_t *module, const int tab);
301
303{
305 if(cst == IOP_CS_NONE)
306 {
307 switch(data->channel_tabs_csp)
308 {
310 cst = IOP_CS_LAB;
311 break;
314 cst = IOP_CS_RGB;
315 break;
318 cst = IOP_CS_NONE;
319 break;
320 }
321 }
322 return cst;
323}
324
326{
328 {
329 switch(mode & ~DEVELOP_BLEND_REVERSE)
330 {
340 return TRUE;
341 default:
342 return FALSE;
343 }
344 }
345 return FALSE;
346}
347
348static inline float _get_boost_factor(const dt_iop_gui_blend_data_t *data, const int channel, const int in_out)
349{
350 return exp2f(data->module->blend_params->blendif_boost_factors[data->channel[channel].param_channels[in_out]]);
351}
352
354 float *out, const dt_iop_order_iccprofile_info_t *work_profile, int in_out)
355{
356 out[0] = out[1] = out[2] = out[3] = out[4] = out[5] = out[6] = out[7] = -1.0f;
357
358 switch(cst)
359 {
360 case IOP_CS_LAB:
361 out[CHANNEL_INDEX_L] = (in[0] / _get_boost_factor(data, 0, in_out)) / 100.0f;
362 out[CHANNEL_INDEX_a] = ((in[1] / _get_boost_factor(data, 1, in_out)) + 128.0f) / 256.0f;
363 out[CHANNEL_INDEX_b] = ((in[2] / _get_boost_factor(data, 2, in_out)) + 128.0f) / 256.0f;
364 break;
365 case IOP_CS_RGB:
367 if(IS_NULL_PTR(work_profile))
368 out[CHANNEL_INDEX_g] = 0.3f * in[0] + 0.59f * in[1] + 0.11f * in[2];
369 else
370 out[CHANNEL_INDEX_g] = dt_ioppr_get_rgb_matrix_luminance(in, work_profile->matrix_in,
371 work_profile->lut_in,
372 work_profile->unbounded_coeffs_in,
373 work_profile->lutsize,
374 work_profile->nonlinearlut);
376 out[CHANNEL_INDEX_R] = in[0] / _get_boost_factor(data, 1, in_out);
377 out[CHANNEL_INDEX_G] = in[1] / _get_boost_factor(data, 2, in_out);
378 out[CHANNEL_INDEX_B] = in[2] / _get_boost_factor(data, 3, in_out);
379 break;
380 case IOP_CS_LCH:
381 out[CHANNEL_INDEX_C] = (in[1] / _get_boost_factor(data, 3, in_out)) / (128.0f * sqrtf(2.0f));
382 out[CHANNEL_INDEX_h] = in[2] / _get_boost_factor(data, 4, in_out);
383 break;
384 case IOP_CS_HSL:
385 out[CHANNEL_INDEX_H] = in[0] / _get_boost_factor(data, 4, in_out);
386 out[CHANNEL_INDEX_S] = in[1] / _get_boost_factor(data, 5, in_out);
387 out[CHANNEL_INDEX_l] = in[2] / _get_boost_factor(data, 6, in_out);
388 break;
389 case IOP_CS_JZCZHZ:
390 out[CHANNEL_INDEX_Jz] = in[0] / _get_boost_factor(data, 4, in_out);
391 out[CHANNEL_INDEX_Cz] = in[1] / _get_boost_factor(data, 5, in_out);
392 out[CHANNEL_INDEX_hz] = in[2] / _get_boost_factor(data, 6, in_out);
393 break;
394 default:
395 break;
396 }
397}
398
399static void _blendif_cook(dt_iop_colorspace_type_t cst, const float *in, float *out,
400 const dt_iop_order_iccprofile_info_t *const work_profile)
401{
402 out[0] = out[1] = out[2] = out[3] = out[4] = out[5] = out[6] = out[7] = -1.0f;
403
404 switch(cst)
405 {
406 case IOP_CS_LAB:
407 out[CHANNEL_INDEX_L] = in[0];
408 out[CHANNEL_INDEX_a] = in[1];
409 out[CHANNEL_INDEX_b] = in[2];
410 break;
411 case IOP_CS_RGB:
413 if(IS_NULL_PTR(work_profile))
414 out[CHANNEL_INDEX_g] = (0.3f * in[0] + 0.59f * in[1] + 0.11f * in[2]) * 100.0f;
415 else
416 out[CHANNEL_INDEX_g] = dt_ioppr_get_rgb_matrix_luminance(in, work_profile->matrix_in,
417 work_profile->lut_in,
418 work_profile->unbounded_coeffs_in,
419 work_profile->lutsize,
420 work_profile->nonlinearlut) * 100.0f;
421 out[CHANNEL_INDEX_R] = in[0] * 100.0f;
422 out[CHANNEL_INDEX_G] = in[1] * 100.0f;
423 out[CHANNEL_INDEX_B] = in[2] * 100.0f;
424 break;
425 case IOP_CS_LCH:
426 out[CHANNEL_INDEX_C] = in[1] / (128.0f * sqrtf(2.0f)) * 100.0f;
427 out[CHANNEL_INDEX_h] = in[2] * 360.0f;
428 break;
429 case IOP_CS_HSL:
430 out[CHANNEL_INDEX_H] = in[0] * 360.0f;
431 out[CHANNEL_INDEX_S] = in[1] * 100.0f;
432 out[CHANNEL_INDEX_l] = in[2] * 100.0f;
433 break;
434 case IOP_CS_JZCZHZ:
435 out[CHANNEL_INDEX_Jz] = in[0] * 100.0f;
436 out[CHANNEL_INDEX_Cz] = in[1] * 100.0f;
437 out[CHANNEL_INDEX_hz] = in[2] * 360.0f;
438 break;
439 default:
440 break;
441 }
442}
443
444static inline int _blendif_print_digits_default(float value)
445{
446 int digits;
447 if(value < 0.0001f) digits = 0;
448 else if(value < 0.01f) digits = 2;
449 else if(value < 0.999f) digits = 1;
450 else digits = 0;
451
452 return digits;
453}
454
455static inline int _blendif_print_digits_ab(float value)
456{
457 int digits;
458 if(fabsf(value) < 10.0f) digits = 1;
459 else digits = 0;
460
461 return digits;
462}
463
464static void _blendif_scale_print_ab(float value, float boost_factor, char *string, int n)
465{
466 const float scaled = (value * 256.0f - 128.0f) * boost_factor;
467 snprintf(string, n, "%-5.*f", _blendif_print_digits_ab(scaled), scaled);
468}
469
470static void _blendif_scale_print_hue(float value, float boost_factor, char *string, int n)
471{
472 snprintf(string, n, "%-5.0f", value * 360.0f);
473}
474
475static void _blendif_scale_print_default(float value, float boost_factor, char *string, int n)
476{
477 const float scaled = value * boost_factor;
478 snprintf(string, n, "%-5.*f", _blendif_print_digits_default(scaled), scaled * 100.0f);
479}
480
483{
484 const gboolean mask_inclusive = blend->mask_combine & DEVELOP_COMBINE_INCL;
485 const uint32_t mask = cst == DEVELOP_BLEND_CS_LAB
488 const uint32_t active_channels = blend->blendif & mask;
489 const uint32_t inverted_channels = (blend->blendif >> 16) ^ (mask_inclusive ? mask : 0);
490 const uint32_t cancel_channels = inverted_channels & ~blend->blendif & mask;
491 return active_channels || cancel_channels;
492}
493
495{
497 if(IS_NULL_PTR(bd) || !bd->blendif_support || !bd->blendif_inited) return FALSE;
498
499 gboolean changed = FALSE;
500 if(!bd->output_channels_shown)
501 {
502 const uint32_t mask = bd->csp == DEVELOP_BLEND_CS_LAB
505
506 dt_develop_blend_params_t *const d = module->blend_params;
507
508 // clear the output channels and invert them when needed
509 const uint32_t old_blendif = d->blendif;
510 const uint32_t need_inversion = d->mask_combine & DEVELOP_COMBINE_INCL ? (mask << 16) : 0;
511
512 d->blendif = (d->blendif & ~(mask | (mask << 16))) | need_inversion;
513
514 changed = (d->blendif != old_blendif);
515
516 for (size_t ch = 0; ch < DEVELOP_BLENDIF_SIZE; ch++)
517 {
518 if ((DEVELOP_BLENDIF_OUTPUT_MASK & (1 << ch))
519 && ( d->blendif_parameters[ch * 4 + 0] != 0.0f
520 || d->blendif_parameters[ch * 4 + 1] != 0.0f
521 || d->blendif_parameters[ch * 4 + 2] != 1.0f
522 || d->blendif_parameters[ch * 4 + 3] != 1.0f))
523 {
524 changed = TRUE;
525 d->blendif_parameters[ch * 4 + 0] = 0.0f;
526 d->blendif_parameters[ch * 4 + 1] = 0.0f;
527 d->blendif_parameters[ch * 4 + 2] = 1.0f;
528 d->blendif_parameters[ch * 4 + 3] = 1.0f;
529 }
530 }
531 }
532 return changed;
533}
534
535static void _blendop_masks_mode_callback(const unsigned int mask_mode, dt_iop_gui_blend_data_t *data)
536{
537 data->module->blend_params->mask_mode = mask_mode;
538
539 dt_iop_set_mask_mode(data->module, mask_mode);
540
541 if(data->blending_box)
542 dt_iop_gui_update_blending(data->module);
543}
544
546{
547 if(darktable.gui->reset) return;
548
549 dt_develop_blend_params_t *bp = data->module->blend_params;
550 dt_develop_blend_mode_t new_blend_mode = GPOINTER_TO_INT(dt_bauhaus_combobox_get_data(combo));
551 if(new_blend_mode != (bp->blend_mode & DEVELOP_BLEND_MODE_MASK))
552 {
553 bp->blend_mode = new_blend_mode | (bp->blend_mode & DEVELOP_BLEND_REVERSE);
555 {
556 gtk_widget_set_visible(data->blend_mode_parameter_slider, TRUE);
557 }
558 else
559 {
560 bp->blend_parameter = 0.0f;
562 gtk_widget_set_visible(data->blend_mode_parameter_slider, FALSE);
563 }
565 dt_iop_gui_update_header(data->module);
566 }
567}
568
570{
571 if(darktable.gui->reset) return;
572
573 dt_develop_blend_params_t *bp = data->module->blend_params;
574 const int val = GPOINTER_TO_INT(dt_bauhaus_combobox_get_data(combobox));
575 if(val == 0)
576 bp->blend_mode &= ~DEVELOP_BLEND_REVERSE;
577 else
579
581 dt_iop_gui_update_header(data->module);
582 dt_control_queue_redraw_widget(GTK_WIDGET(combobox));
583}
584
586{
587 dt_develop_blend_params_t *const d = data->module->blend_params;
588
589 const unsigned combine = GPOINTER_TO_UINT(dt_bauhaus_combobox_get_data(data->masks_combine_combo));
590 d->mask_combine &= ~(DEVELOP_COMBINE_INV | DEVELOP_COMBINE_INCL);
591 d->mask_combine |= combine;
592
593 // inverts the parametric mask channels that are not used
594 if(data->blendif_support && data->blendif_inited)
595 {
597 const uint32_t unused_channels = mask & ~d->blendif;
598 d->blendif &= ~(unused_channels << 16);
599 if(d->mask_combine & DEVELOP_COMBINE_INCL)
600 {
601 d->blendif |= unused_channels << 16;
602 }
603 _blendop_blendif_update_tab(data->module, data->tab);
604 }
605
606 _blendif_clean_output_channels(data->module);
608 dt_iop_gui_update_header(data->module);
609}
610
611static void _blendop_masks_invert_toggled(GtkToggleButton *togglebutton, dt_iop_gui_blend_data_t *data)
612{
613 if(darktable.gui->reset) return;
614 const gboolean active = gtk_toggle_button_get_active(togglebutton);
615 if(active)
616 data->module->blend_params->mask_combine |= DEVELOP_COMBINE_INV;
617 else
618 data->module->blend_params->mask_combine &= ~DEVELOP_COMBINE_INV;
619 _blendif_clean_output_channels(data->module);
621 dt_iop_gui_update_header(data->module);
622}
623
625{
626 if(darktable.gui->reset) return;
627
628 dt_develop_blend_params_t *bp = data->module->blend_params;
629
630 const dt_iop_gui_blendif_channel_t *channel = &data->channel[data->tab];
631
632 const int in_out = (slider == data->filter[1].slider) ? 1 : 0;
634 GtkLabel **label = data->filter[in_out].label;
635
636 if(!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->colorpicker))
637 && !gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->colorpicker_set_values)))
638 {
639 dt_iop_color_picker_reset(data->module, FALSE);
640 }
641
642 float *parameters = &(bp->blendif_parameters[4 * ch]);
643
645 for(int k = 0; k < 4; k++) parameters[k] = dtgtk_gradient_slider_multivalue_get_value(slider, k);
647
648 const float boost_factor = _get_boost_factor(data, data->tab, in_out);
649 for(int k = 0; k < 4; k++)
650 {
651 char text[256];
652 (channel->scale_print)(parameters[k], boost_factor, text, sizeof(text));
653 gtk_label_set_text(label[k], text);
654 }
655
657 if(parameters[1] == 0.0f && parameters[2] == 1.0f)
658 bp->blendif &= ~(1 << ch);
659 else
660 bp->blendif |= (1 << ch);
661
663 dt_iop_gui_update_header(data->module);
664}
665
668{
669 if(darktable.gui->reset) return;
670
671 dt_develop_blend_params_t *bp = data->module->blend_params;
672
673 const dt_iop_gui_blendif_channel_t *channel = &data->channel[data->tab];
674
675 const int in_out = (slider == data->filter[1].slider) ? 1 : 0;
677
678 // invert the parametric mask if needed
680 bp->blendif |= (1 << (16 + ch));
681 else
682 bp->blendif &= ~(1 << (16 + ch));
683
685 dt_iop_gui_update_header(data->module);
686 _blendop_blendif_update_tab(data->module, data->tab);
687}
688
689static void _blendop_blendif_polarity_callback(GtkToggleButton *togglebutton, dt_iop_gui_blend_data_t *data)
690{
691 if(darktable.gui->reset) return;
692
693 int active = gtk_toggle_button_get_active(togglebutton);
694
695 dt_develop_blend_params_t *bp = data->module->blend_params;
696
697 const dt_iop_gui_blendif_channel_t *channel = &data->channel[data->tab];
698
699 const int in_out = (GTK_WIDGET(togglebutton) == data->filter[1].polarity) ? 1 : 0;
701 GtkDarktableGradientSlider *slider = data->filter[in_out].slider;
702
703 if(!active)
704 bp->blendif |= (1 << (ch + 16));
705 else
706 bp->blendif &= ~(1 << (ch + 16));
707
716
718 dt_iop_gui_update_header(data->module);
719 dt_control_queue_redraw_widget(GTK_WIDGET(togglebutton));
720}
721
722static float log10_scale_callback(GtkWidget *self, float inval, int dir)
723{
724 float outval;
725 const float tiny = 1.0e-4f;
726 switch(dir)
727 {
729 outval = (log10(CLAMP(inval, 0.0001f, 1.0f)) + 4.0f) / 4.0f;
730 break;
732 outval = CLAMP(exp(M_LN10 * (4.0f * inval - 4.0f)), 0.0f, 1.0f);
733 if(outval <= tiny) outval = 0.0f;
734 if(outval >= 1.0f - tiny) outval = 1.0f;
735 break;
736 default:
737 outval = inval;
738 }
739 return outval;
740}
741
742
743static float magnifier_scale_callback(GtkWidget *self, float inval, int dir)
744{
745 float outval;
746 const float range = 6.0f;
747 const float invrange = 1.0f/range;
748 const float scale = tanh(range * 0.5f);
749 const float invscale = 1.0f/scale;
750 const float eps = 1.0e-6f;
751 const float tiny = 1.0e-4f;
752 switch(dir)
753 {
755 outval = (invscale * tanh(range * (CLAMP(inval, 0.0f, 1.0f) - 0.5f)) + 1.0f) * 0.5f;
756 if(outval <= tiny) outval = 0.0f;
757 if(outval >= 1.0f - tiny) outval = 1.0f;
758 break;
760 outval = invrange * atanh((2.0f * CLAMP(inval, eps, 1.0f - eps) - 1.0f) * scale) + 0.5f;
761 if(outval <= tiny) outval = 0.0f;
762 if(outval >= 1.0f - tiny) outval = 1.0f;
763 break;
764 default:
765 outval = inval;
766 }
767 return outval;
768}
769
771 float (*scale_callback)(GtkWidget*, float, int), const char *label)
772{
773 dt_iop_gui_blend_data_t *data = module->blend_data;
775
776 int in_out = (slider == data->filter[1].slider) ? 1 : 0;
777
778 dtgtk_gradient_slider_multivalue_set_scale_callback(slider, (mode == 1) ? scale_callback : NULL);
779 gchar *text = g_strdup_printf("%s%s",
780 (in_out == 0) ? _("input") : _("output"),
781 (mode == 1) ? label : "");
782 gtk_label_set_text(data->filter[in_out].head, text);
783 dt_free(text);
784
785 return (mode == 1) ? 1 : 0;
786}
787
788
790{
791 return _blendop_blendif_disp_alternative_worker(widget, module, mode, magnifier_scale_callback, _(" (zoom)"));
792}
793
795{
796 return _blendop_blendif_disp_alternative_worker(widget, module, mode, log10_scale_callback, _(" (log)"));
797}
798
800{
801 (void) _blendop_blendif_disp_alternative_worker(widget, module, 0, NULL, "");
802}
803
811static void _blendop_blendif_log_scale_toggled(GtkToggleButton *button, dt_iop_module_t *module)
812{
813 if(darktable.gui->reset) return;
814
815 dt_iop_gui_blend_data_t *data = !IS_NULL_PTR(module) ? module->blend_data : NULL;
816 if(IS_NULL_PTR(data) || IS_NULL_PTR(data->channel)) return;
817
818 const int in_out = GTK_WIDGET(button) == data->filter[1].log_scale ? 1 : 0;
819 const dt_iop_gui_blendif_channel_t *channel = &data->channel[data->tab];
820 if(IS_NULL_PTR(channel->altdisplay)) return;
821
822 const gboolean active = gtk_toggle_button_get_active(button);
823 gtk_button_set_label(GTK_BUTTON(button), active ? _("Linear scale") : _("Log scale"));
824
825 const int requested_mode = active ? 1 : 0;
826 data->altmode[data->tab][in_out]
827 = channel->altdisplay(GTK_WIDGET(data->filter[in_out].slider), module, requested_mode);
828}
829
830
832{
834
836 {
837 if(bd->tab < 4)
838 picker_cst = IOP_CS_RGB;
839 else
840 picker_cst = IOP_CS_HSL;
841 }
843 {
844 if(bd->tab < 4)
845 picker_cst = IOP_CS_RGB;
846 else
847 picker_cst = IOP_CS_JZCZHZ;
848 }
850 {
851 if(bd->tab < 3)
852 picker_cst = IOP_CS_LAB;
853 else
854 picker_cst = IOP_CS_LCH;
855 }
856
857 return picker_cst;
858}
859
860static inline int _blendif_print_digits_picker(float value)
861{
862 int digits;
863 if(value < 10.0f) digits = 2;
864 else digits = 1;
865
866 return digits;
867}
868
869static void _update_gradient_slider_pickers(GtkWidget *callback_dummy, dt_iop_module_t *module)
870{
871 dt_iop_gui_blend_data_t *data = module->blend_data;
872 if(callback_dummy == data->colorpicker_set_values
873 && !gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->colorpicker_set_values)))
874 {
877 }
878
880
881 float *raw_mean, *raw_min, *raw_max;
882
884
885 for(int in_out = 1; in_out >= 0; in_out--)
886 {
887 if(in_out)
888 {
889 raw_mean = module->picked_output_color;
890 raw_min = module->picked_output_color_min;
891 raw_max = module->picked_output_color_max;
892 }
893 else
894 {
895 raw_mean = module->picked_color;
896 raw_min = module->picked_color_min;
897 raw_max = module->picked_color_max;
898 }
899
900 if((gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->colorpicker)) ||
901 gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->colorpicker_set_values))) &&
902 (raw_min[0] != INFINITY))
903 {
904 float picker_mean[8], picker_min[8], picker_max[8];
905 float cooked[8];
906
907 const dt_develop_blend_colorspace_t blend_csp = data->channel_tabs_csp;
909 const dt_iop_order_iccprofile_info_t *work_profile = (blend_csp == DEVELOP_BLEND_CS_RGB_SCENE)
911 : dt_ioppr_get_iop_work_profile_info(module, module->dev->iop);
912
913 _blendif_scale(data, cst, raw_mean, picker_mean, work_profile, in_out);
914 _blendif_scale(data, cst, raw_min, picker_min, work_profile, in_out);
915 _blendif_scale(data, cst, raw_max, picker_max, work_profile, in_out);
916 _blendif_cook(cst, raw_mean, cooked, work_profile);
917
918 gchar *text = g_strdup_printf("(%.*f)", _blendif_print_digits_picker(cooked[data->tab]), cooked[data->tab]);
919
921 data->filter[in_out].slider,
922 CLAMP(picker_mean[data->tab], 0.0f, 1.0f),
923 CLAMP(picker_min[data->tab], 0.0f, 1.0f),
924 CLAMP(picker_max[data->tab], 0.0f, 1.0f));
925 gtk_label_set_text(data->filter[in_out].picker_label, text);
926
927 dt_free(text);
928 }
929 else
930 {
932 gtk_label_set_text(data->filter[in_out].picker_label, "");
933 }
934 }
935
937}
938
939
940static void _blendop_blendif_update_tab(dt_iop_module_t *module, const int tab)
941{
942 dt_iop_gui_blend_data_t *data = module->blend_data;
943 dt_develop_blend_params_t *bp = module->blend_params;
944 dt_develop_blend_params_t *dp = module->default_blendop_params;
945 const float epsilon = 1e-6f;
946
948
949 const dt_iop_gui_blendif_channel_t *channel = &data->channel[tab];
950
951 for(int in_out = 1; in_out >= 0; in_out--)
952 {
953 const dt_develop_blendif_channels_t ch = channel->param_channels[in_out];
954 dt_iop_gui_blendif_filter_t *sl = &data->filter[in_out];
955
956 float *parameters = &(bp->blendif_parameters[4 * ch]);
957 float *defaults = &(dp->blendif_parameters[4 * ch]);
958
959 const int polarity = !(bp->blendif & (1 << (ch + 16)));
960
961 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(sl->polarity), polarity);
962
964 sl->slider,
967 sl->slider,
970 sl->slider,
973 sl->slider,
975
977 for(int k = 0; k < 4; k++)
978 {
981 }
983
984 const float boost_factor = _get_boost_factor(data, tab, in_out);
985 for(int k = 0; k < 4; k++)
986 {
987 char text[256];
988 channel->scale_print(parameters[k], boost_factor, text, sizeof(text));
989 gtk_label_set_text(sl->label[k], text);
990 }
991
993
994 for(int k = 0; k < channel->numberstops; k++)
995 {
997 channel->colorstops[k].color);
998 }
999
1001
1002 if(channel->altdisplay)
1003 {
1004 data->altmode[tab][in_out] = channel->altdisplay(GTK_WIDGET(sl->slider), module, data->altmode[tab][in_out]);
1005 gtk_widget_set_sensitive(sl->log_scale, TRUE);
1006 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(sl->log_scale), data->altmode[tab][in_out] == 1);
1007 gtk_button_set_label(GTK_BUTTON(sl->log_scale),
1008 data->altmode[tab][in_out] == 1 ? _("Linear scale") : _("Log scale"));
1009 }
1010 else
1011 {
1012 _blendop_blendif_disp_alternative_reset(GTK_WIDGET(sl->slider), module);
1013 data->altmode[tab][in_out] = 0;
1014 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(sl->log_scale), FALSE);
1015 gtk_button_set_label(GTK_BUTTON(sl->log_scale), _("Log scale"));
1016 gtk_widget_set_sensitive(sl->log_scale, FALSE);
1017 }
1018 }
1019
1020 _update_gradient_slider_pickers(NULL, module);
1021
1022 const gboolean boost_factor_enabled = channel->boost_factor_enabled;
1023 float boost_factor = 0.0f;
1024 if(boost_factor_enabled)
1025 {
1026 boost_factor = bp->blendif_boost_factors[channel->param_channels[0]] - channel->boost_factor_offset;
1027 }
1028 gtk_widget_set_sensitive(GTK_WIDGET(data->channel_boost_factor_slider), boost_factor_enabled);
1029 dt_bauhaus_slider_set(GTK_WIDGET(data->channel_boost_factor_slider), boost_factor);
1030
1031 for(int page_num = 0; !IS_NULL_PTR(data->channel[page_num].label); page_num++)
1032 {
1033 gboolean modified = FALSE;
1034 const dt_iop_gui_blendif_channel_t *page_channel = &data->channel[page_num];
1035
1036 for(int in_out = 0; in_out < 2 && !modified; in_out++)
1037 {
1038 const dt_develop_blendif_channels_t ch = page_channel->param_channels[in_out];
1039
1040 const float *params = &bp->blendif_parameters[4 * ch];
1041 const float *defaults = &dp->blendif_parameters[4 * ch];
1042 for(int k = 0; k < 4; k++)
1043 {
1044 if(fabsf(params[k] - defaults[k]) > epsilon)
1045 {
1046 modified = TRUE;
1047 break;
1048 }
1049 }
1050 }
1051
1052 GtkWidget *page = gtk_notebook_get_nth_page(data->channel_tabs, page_num);
1053 GtkWidget *label = GTK_IS_WIDGET(page) ? gtk_notebook_get_tab_label(data->channel_tabs, page) : NULL;
1054 if(GTK_IS_LABEL(label))
1055 {
1056 gchar *tab_title = modified
1057 ? g_markup_printf_escaped("<b>%s</b>", _(page_channel->label))
1058 : g_markup_printf_escaped("%s", _(page_channel->label));
1059 gtk_label_set_markup(GTK_LABEL(label), tab_title);
1060 dt_free(tab_title);
1061 }
1062 }
1063
1065 {
1066 const gboolean output
1067 = (module->request_mask_display & DT_DEV_PIXELPIPE_DISPLAY_OUTPUT) != 0;
1068 dt_dev_pixelpipe_display_mask_t new_request_mask_display
1069 = module->request_mask_display & ~DT_DEV_PIXELPIPE_DISPLAY_ANY;
1070 new_request_mask_display |= channel->display_channel;
1071 if(output) new_request_mask_display |= DT_DEV_PIXELPIPE_DISPLAY_OUTPUT;
1072
1073 if(new_request_mask_display != module->request_mask_display)
1074 {
1075 module->request_mask_display = new_request_mask_display;
1078 }
1079 }
1080
1082 --darktable.gui->reset;
1083}
1084
1085
1086static void _blendop_blendif_tab_switch(GtkNotebook *notebook, GtkWidget *page, guint page_num,
1088{
1089 if(darktable.gui->reset || IS_NULL_PTR(data) || !data->blendif_inited) return;
1090 const int cst_old = _blendop_blendif_get_picker_colorspace(data);
1091 dt_iop_color_picker_reset(data->module, FALSE);
1092
1093 data->tab = page_num;
1094
1095 if(cst_old != _blendop_blendif_get_picker_colorspace(data)
1096 && (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->colorpicker))
1097 || gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->colorpicker_set_values))))
1098 {
1100 dt_dev_pixelpipe_update_history_all(data->module->dev);
1101 }
1102
1103 _blendop_blendif_update_tab(data->module, data->tab);
1104}
1105
1112static void _blendop_blending_notebook_switch(GtkNotebook *notebook, GtkWidget *page, guint page_num,
1114{
1115 (void)notebook;
1116 (void)page;
1117 if(IS_NULL_PTR(data)) return;
1118
1120}
1121
1123{
1124 if(darktable.gui->reset || IS_NULL_PTR(data) || !data->blendif_inited) return;
1125 dt_develop_blend_params_t *bp = data->module->blend_params;
1126 const int tab = data->tab;
1127
1128 const float value = dt_bauhaus_slider_get(slider);
1129 for(int in_out = 1; in_out >= 0; in_out--)
1130 {
1131 const int ch = data->channel[tab].param_channels[in_out];
1132 float off = 0.0f;
1135 {
1136 off = 0.5f;
1137 }
1138 const float new_value = value + data->channel[tab].boost_factor_offset;
1139 const float old_value = bp->blendif_boost_factors[ch];
1140 const float factor = exp2f(old_value) / exp2f(new_value);
1141 float *parameters = &(bp->blendif_parameters[4 * ch]);
1142 if(parameters[0] > 0.0f) parameters[0] = clamp_range_f((parameters[0] - off) * factor + off, 0.0f, 1.0f);
1143 if(parameters[1] > 0.0f) parameters[1] = clamp_range_f((parameters[1] - off) * factor + off, 0.0f, 1.0f);
1144 if(parameters[2] < 1.0f) parameters[2] = clamp_range_f((parameters[2] - off) * factor + off, 0.0f, 1.0f);
1145 if(parameters[3] < 1.0f) parameters[3] = clamp_range_f((parameters[3] - off) * factor + off, 0.0f, 1.0f);
1146 if(parameters[1] == 0.0f && parameters[2] == 1.0f)
1147 bp->blendif &= ~(1 << ch);
1148 bp->blendif_boost_factors[ch] = new_value;
1149 }
1150
1151 if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->colorpicker_set_values)))
1152 {
1154 }
1155
1156 _blendop_blendif_update_tab(data->module, tab);
1157
1159 dt_iop_gui_update_header(data->module);
1160}
1161
1163{
1164 if(darktable.gui->reset || IS_NULL_PTR(data) || !data->blendif_inited) return;
1165 dt_develop_blend_params_t *bp = data->module->blend_params;
1166 const float oldval = bp->details;
1167 bp->details = dt_bauhaus_slider_get(slider);
1169 dt_iop_gui_update_header(data->module);
1170
1171 if((oldval == 0.0f) && (bp->details != 0.0f))
1172 {
1173 dt_dev_pixelpipe_update_history_all(data->module->dev);
1174 }
1175}
1176
1177static gboolean _blendop_blendif_showmask_clicked(GtkToggleButton *button, GdkEventButton *event, dt_iop_module_t *module)
1178{
1179 // FIXME : there are more than 3 functions getting clever about how to setup module->request_mask_display depending on user input.
1180 // These should all use an uniform setter function.
1181 //
1182 // The lack of setter implies we don't guaranty that only 1 module can request mask display at a time.
1183 // The consequence is pipeline needs to check if module->request_mask_display AND module == dev->gui_module,
1184 // but the global pipe->mask_display is set from *_blend_process() at runtime, so it's a pipe property
1185 // that changes over the pipe lifecycle.
1186 //
1187 // This is a self-feeding loop of madness because it ties the pipeline to GUI states
1188 // (but not all pipes are connected to a GUI, so you need to cover all cases all the time and don't forget to test everything),
1189 // and because the pipeline is executed recursively from the end, but pipe->mask_display is set in the middle,
1190 // when it reaches the process() method of the module capturing mask preview, so you don't have this info when
1191 // planning for pipeline execution.
1192 // And you need to plan for mask preview ahead in pipe because mask preview needs to work
1193 // without using the pixelpipe cache, at least between the module requiring mask preview and gamma.c, which will actually render
1194 // the preview at the far end of the pipe.
1195 // So the not-so-clever workaround inherited from darktable was to flush all cache lines when requesting mask preview,
1196 // which flushed lines that could be reused later and were only temporarily not needed.
1197 if(darktable.gui->reset) return TRUE;
1198
1199 if(event->button == 1)
1200 {
1201 const int has_mask_display = module->request_mask_display & (DT_DEV_PIXELPIPE_DISPLAY_MASK | DT_DEV_PIXELPIPE_DISPLAY_CHANNEL);
1202
1203 module->request_mask_display
1204 &= ~(DT_DEV_PIXELPIPE_DISPLAY_MASK | DT_DEV_PIXELPIPE_DISPLAY_CHANNEL
1205 | DT_DEV_PIXELPIPE_DISPLAY_ANY | DT_DEV_PIXELPIPE_DISPLAY_STICKY);
1206
1207 if(dt_modifier_is(event->state, GDK_CONTROL_MASK | GDK_SHIFT_MASK))
1208 module->request_mask_display |= (DT_DEV_PIXELPIPE_DISPLAY_MASK | DT_DEV_PIXELPIPE_DISPLAY_CHANNEL);
1209 else if(dt_modifier_is(event->state, GDK_SHIFT_MASK))
1210 module->request_mask_display |= DT_DEV_PIXELPIPE_DISPLAY_CHANNEL;
1211 else if(dt_modifier_is(event->state, GDK_CONTROL_MASK))
1212 module->request_mask_display |= DT_DEV_PIXELPIPE_DISPLAY_MASK;
1213 else
1214 module->request_mask_display |= (has_mask_display ? 0 : DT_DEV_PIXELPIPE_DISPLAY_MASK);
1215
1216 gtk_toggle_button_set_active(button,
1218
1219 module->enabled = TRUE;
1220 if(module->off) gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(module->off), TRUE);
1221
1222 ++darktable.gui->reset;
1223 // (re)set the header mask indicator too
1224 if(module->mask_indicator)
1225 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(module->mask_indicator),
1227 --darktable.gui->reset;
1228
1231 dt_iop_request_focus(module);
1232
1233 // We don't want to re-read the history here
1235 }
1236
1237 return TRUE;
1238}
1239
1240static void _blendop_masks_mode_changed(GtkToggleButton *togglebutton, dt_iop_module_t *module)
1241{
1242 if(darktable.gui->reset) return;
1243
1244 const unsigned int bit = GPOINTER_TO_UINT(g_object_get_data(G_OBJECT(togglebutton), "mask-bit"));
1245 if(!bit) return;
1246 if(IS_NULL_PTR(module)) return;
1247 if(IS_NULL_PTR(module->blend_data)) return;
1248
1249 uint32_t mask_mode = module->blend_params->mask_mode;
1250 const gboolean active = gtk_toggle_button_get_active(togglebutton);
1251 const gboolean is_disable_toggle = bit == DEVELOP_MASK_RASTER
1252 || bit == DEVELOP_MASK_SHAPE
1253 || bit == DEVELOP_MASK_PARAMETRIC;
1254 const gboolean enable_bit = is_disable_toggle ? !active : active;
1255
1256 switch(bit)
1257 {
1259 if(enable_bit)
1260 mask_mode |= DEVELOP_MASK_ENABLED;
1261 else
1262 mask_mode &= ~DEVELOP_MASK_ENABLED;
1263 break;
1264
1266 if(enable_bit)
1268 else
1269 mask_mode &= ~DEVELOP_MASK_RASTER;
1270 break;
1271
1272 case DEVELOP_MASK_SHAPE:
1273 if(enable_bit)
1275 else
1276 mask_mode &= ~DEVELOP_MASK_SHAPE;
1277 break;
1278
1280 if(enable_bit)
1282 else
1283 mask_mode &= ~DEVELOP_MASK_PARAMETRIC;
1284 break;
1285
1286 default:
1287 break;
1288 }
1289
1290 dt_iop_gui_blend_data_t *data = module->blend_data;
1291 _blendop_masks_mode_callback(mask_mode, data);
1295 dt_iop_gui_update_header(data->module);
1296}
1297
1298static gboolean _blendop_blendif_reset(GtkButton *button, GdkEventButton *event, dt_iop_module_t *module)
1299{
1300 module->blend_params->blendif = module->default_blendop_params->blendif;
1302 4 * DEVELOP_BLENDIF_SIZE * sizeof(float));
1303 module->blend_params->details = module->default_blendop_params->details;
1304
1309
1310 return TRUE;
1311}
1312
1313static gboolean _blendop_blendif_invert(GtkButton *button, GdkEventButton *event, dt_iop_module_t *module)
1314{
1315 if(darktable.gui->reset) return TRUE;
1316
1317 const dt_iop_gui_blend_data_t *data = module->blend_data;
1318
1319 unsigned int toggle_mask = 0;
1320
1321 switch(data->channel_tabs_csp)
1322 {
1324 toggle_mask = DEVELOP_BLENDIF_Lab_MASK << 16;
1325 break;
1328 toggle_mask = DEVELOP_BLENDIF_RGB_MASK << 16;
1329 break;
1332 toggle_mask = 0;
1333 break;
1334 }
1335
1336 module->blend_params->blendif ^= toggle_mask;
1337 module->blend_params->mask_combine ^= DEVELOP_COMBINE_MASKS_POS;
1338 module->blend_params->mask_combine ^= DEVELOP_COMBINE_INCL;
1342
1343 return TRUE;
1344}
1345
1347 dt_masks_type_t type, gpointer user_data)
1348{
1350 if(IS_NULL_PTR(module) || IS_NULL_PTR(bd)) return FALSE;
1351
1352 // we want to be sure that the iop has focus
1353 dt_iop_request_focus(module);
1356 if(GTK_IS_TOGGLE_BUTTON(bd->masks_edit))
1357 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(bd->masks_edit), FALSE);
1358 return TRUE;
1359}
1360
1361static gboolean _blendop_masks_show_and_edit(GtkWidget *widget, GdkEventButton *event, dt_iop_module_t *self)
1362{
1363 if(darktable.gui->reset) return FALSE;
1365
1366 if(event->button == 1)
1367 {
1368 ++darktable.gui->reset;
1369
1372
1374 if(grp && (grp->type & DT_MASKS_GROUP) && grp->points)
1375 {
1376 const gboolean control_button_pressed = dt_modifier_is(event->state, GDK_CONTROL_MASK);
1377
1378 switch(bd->masks_shown)
1379 {
1380 case DT_MASKS_EDIT_FULL:
1381 bd->masks_shown = control_button_pressed ? DT_MASKS_EDIT_RESTRICTED : DT_MASKS_EDIT_OFF;
1382 break;
1383
1385 bd->masks_shown = !control_button_pressed ? DT_MASKS_EDIT_FULL : DT_MASKS_EDIT_OFF;
1386 break;
1387
1388 default:
1389 case DT_MASKS_EDIT_OFF:
1390 bd->masks_shown = control_button_pressed ? DT_MASKS_EDIT_RESTRICTED : DT_MASKS_EDIT_FULL;
1391 }
1392 }
1393 else
1394 {
1396 /* remove hinter messages */
1398 }
1399
1400 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(bd->masks_edit), bd->masks_shown != DT_MASKS_EDIT_OFF);
1402
1403 // Deactivate every masks shape toolbar because edit mode replaces creation mode.
1405
1406 --darktable.gui->reset;
1407
1408 return TRUE;
1409 }
1410
1411 return FALSE;
1412}
1413
1414static gboolean _blendop_masks_polarity_callback(GtkToggleButton *togglebutton, GdkEventButton *event, dt_iop_module_t *self)
1415{
1416 if(darktable.gui->reset) return TRUE;
1417
1418 const int active = !gtk_toggle_button_get_active(togglebutton);
1419 gtk_toggle_button_set_active(togglebutton, active);
1420
1422
1423 if(active)
1425 else
1426 bp->mask_combine &= ~DEVELOP_COMBINE_MASKS_POS;
1427
1430 dt_control_queue_redraw_widget(GTK_WIDGET(togglebutton));
1431
1432 return TRUE;
1433}
1434
1445
1457
1459{
1460 if(IS_NULL_PTR(module)) return NULL;
1461 if(IS_NULL_PTR(module->blend_params)) return NULL;
1463}
1464
1466{
1467 if(IS_NULL_PTR(mask_form)) return;
1468
1469 int new_form_id = 100;
1470 for(GList *form_node = darktable.develop->forms; form_node;)
1471 {
1472 const dt_masks_form_t *existing_form = (dt_masks_form_t *)form_node->data;
1473 if(existing_form->formid == mask_form->formid)
1474 {
1475 mask_form->formid = new_form_id++;
1476 form_node = darktable.develop->forms;
1477 }
1478 else
1479 {
1480 form_node = g_list_next(form_node);
1481 }
1482 }
1483}
1484
1486{
1487 if(IS_NULL_PTR(module)) return NULL;
1488
1490 if(IS_NULL_PTR(group_form)) return NULL;
1491
1492 //gchar *module_label = dt_history_item_get_name(module);
1493 gchar *module_label = g_strdup(module->multi_name);
1494 if(g_strcmp0(module_label, "") == 0)
1495 {
1496 dt_free(module_label);
1497 module_label = dt_history_item_get_name(module);
1498 }
1499 g_snprintf(group_form->name, sizeof(group_form->name), "%s %s", _("Mask"), module_label);
1500 dt_free(module_label);
1501
1502 _blendop_masks_check_id(group_form);
1504 module->blend_params->mask_id = group_form->formid;
1505
1506 return group_form;
1507}
1508
1509static dt_masks_form_group_t *_blendop_masks_find_group_entry(dt_masks_form_t *group_form, const int formid, int *index)
1510{
1511 if(!IS_NULL_PTR(index)) *index = -1;
1512 if(IS_NULL_PTR(group_form)) return NULL;
1513 if(!(group_form->type & DT_MASKS_GROUP)) return NULL;
1514
1515 int group_index = 0;
1516 for(GList *group_node = group_form->points; group_node; group_node = g_list_next(group_node))
1517 {
1518 dt_masks_form_group_t *group_entry = (dt_masks_form_group_t *)group_node->data;
1519 if(group_entry->formid == formid)
1520 {
1521 if(index) *index = group_index;
1522 return group_entry;
1523 }
1524 group_index++;
1525 }
1526
1527 return NULL;
1528}
1529
1531{
1532 if(IS_NULL_PTR(bd)) return;
1533
1534 // Only initialize icons if they haven't been created yet
1537 return;
1538
1539 const int icon_size = DT_PIXEL_APPLY_DPI(13);
1550}
1551
1552static const GdkPixbuf *_blendop_masks_get_op_icon(const dt_iop_gui_blend_data_t *bd, const int state,
1553 const int index)
1554{
1555 if(IS_NULL_PTR(bd) || index == 0) return NULL;
1556
1557 if(state & DT_MASKS_STATE_UNION) return bd->masks_ic_union;
1561 return NULL;
1562}
1563
1564static const GdkPixbuf *_blendop_masks_get_inverse_icon(const dt_iop_gui_blend_data_t *bd, const int state)
1565{
1566 return (bd && (state & DT_MASKS_STATE_INVERSE)) ? bd->masks_ic_inverse : NULL;
1567}
1568
1569static int _blendop_masks_group_tree_append(const dt_iop_gui_blend_data_t *bd, GtkTreeStore *tree_store,
1570 GtkTreeIter *parent_iter, const dt_masks_form_t *parent_group);
1571
1572// Check if this is a parent mask reusing a single parent mask (and no shapes).
1573// Such a wrapper is redundant and should be hidden: the child mask should be used directly instead.
1575{
1576 if(IS_NULL_PTR(mask_form)) return FALSE;
1577 if(!(mask_form->type & DT_MASKS_GROUP)) return FALSE;
1578 if(IS_NULL_PTR(mask_form->points)) return FALSE;
1579
1580 // Check if there is exactly one item
1581 if(g_list_length(mask_form->points) != 1) return FALSE;
1582
1583 // Get the single child
1584 const dt_masks_form_group_t *group_entry = (const dt_masks_form_group_t *)mask_form->points->data;
1585 if(IS_NULL_PTR(group_entry)) return FALSE;
1586
1587 const dt_masks_form_t *child_form = dt_masks_get_from_id(darktable.develop, group_entry->formid);
1588 if(IS_NULL_PTR(child_form)) return FALSE;
1589
1590 // Check if the single child is a group
1591 return (child_form->type & DT_MASKS_GROUP) ? TRUE : FALSE;
1592}
1593
1595{
1596 if(IS_NULL_PTR(mask_form)) return FALSE;
1597 if(!(mask_form->type & DT_MASKS_GROUP)) return FALSE;
1598 if(IS_NULL_PTR(mask_form->points)) return FALSE;
1599
1600 for(const GList *group_node = mask_form->points; group_node; group_node = g_list_next(group_node))
1601 {
1602 const dt_masks_form_group_t *group_entry = (const dt_masks_form_group_t *)group_node->data;
1603 if(IS_NULL_PTR(group_entry)) continue;
1604
1605 const dt_masks_form_t *child_form = dt_masks_get_from_id(darktable.develop, group_entry->formid);
1606 if(IS_NULL_PTR(child_form)) continue;
1607 if(!(child_form->type & DT_MASKS_GROUP)) return TRUE;
1608 if(_blendop_masks_is_group_with_shapes(child_form)) return TRUE;
1609 }
1610
1611 return FALSE;
1612}
1613
1614static int _blendop_masks_group_tree_append_entry(const dt_iop_gui_blend_data_t *bd, GtkTreeStore *tree_store,
1615 GtkTreeIter *parent_iter,
1616 const dt_masks_form_group_t *group_entry,
1617 const dt_masks_form_t *mask_form, const int index)
1618{
1619 if(IS_NULL_PTR(bd) || IS_NULL_PTR(tree_store) || IS_NULL_PTR(group_entry) || IS_NULL_PTR(mask_form)) return 0;
1620
1621 GtkTreeIter iter;
1622
1623 // Append the opacity to the displayed name, same as the masks library treeview.
1624 char display_name[256] = "";
1625 if(group_entry->opacity != 1.0f)
1626 g_snprintf(display_name, sizeof(display_name), "%s %d%%", mask_form->name,
1627 (int)(group_entry->opacity * 100));
1628 else
1629 g_strlcpy(display_name, mask_form->name, sizeof(display_name));
1630
1631 gtk_tree_store_append(tree_store, &iter, parent_iter);
1632 gtk_tree_store_set(tree_store, &iter, BLENDOP_MASKS_GROUP_COL_OP_ICON,
1633 _blendop_masks_get_op_icon(bd, group_entry->state, index),
1635 _blendop_masks_get_inverse_icon(bd, group_entry->state),
1636 BLENDOP_MASKS_GROUP_COL_NAME, display_name,
1641
1642 int row_count = 1;
1643 if(mask_form->type & DT_MASKS_GROUP)
1644 row_count += _blendop_masks_group_tree_append(bd, tree_store, &iter, mask_form);
1645
1646 return row_count;
1647}
1648
1649static int _blendop_masks_group_tree_append(const dt_iop_gui_blend_data_t *bd, GtkTreeStore *tree_store,
1650 GtkTreeIter *parent_iter, const dt_masks_form_t *parent_group)
1651{
1652 if(IS_NULL_PTR(bd)) return 0;
1653 if(IS_NULL_PTR(tree_store)) return 0;
1654 if(IS_NULL_PTR(parent_group)) return 0;
1655 if(!(parent_group->type & DT_MASKS_GROUP)) return 0;
1656
1657 int row_count = 0;
1658
1659 // First pass: groups containing shapes.
1660 int index = 0;
1661 for(const GList *group_node = parent_group->points; group_node; group_node = g_list_next(group_node))
1662 {
1663 const dt_masks_form_group_t *group_entry = (const dt_masks_form_group_t *)group_node->data;
1664 const dt_masks_form_t *mask_form = group_entry
1666 : NULL;
1667 if(mask_form && _blendop_masks_is_group_with_shapes(mask_form))
1668 row_count += _blendop_masks_group_tree_append_entry(bd, tree_store, parent_iter, group_entry, mask_form,
1669 index);
1670 index++;
1671 }
1672
1673 // Second pass: all remaining entries.
1674 index = 0;
1675 for(const GList *group_node = parent_group->points; group_node; group_node = g_list_next(group_node))
1676 {
1677 const dt_masks_form_group_t *group_entry = (const dt_masks_form_group_t *)group_node->data;
1678 const dt_masks_form_t *mask_form = group_entry
1680 : NULL;
1681 if(mask_form && !_blendop_masks_is_group_with_shapes(mask_form))
1682 {
1683 row_count += _blendop_masks_group_tree_append_entry(bd, tree_store, parent_iter, group_entry, mask_form,
1684 index);
1685 }
1686 index++;
1687 }
1688
1689 return row_count;
1690}
1691
1692static gboolean _blendop_masks_find_iter_by_formid(GtkTreeModel *model, GtkTreeIter *iter,
1693 const int formid_col, const int formid)
1694{
1695 if(IS_NULL_PTR(model) || IS_NULL_PTR(iter)) return FALSE;
1696
1697 gboolean valid = gtk_tree_model_get_iter_first(model, iter);
1698 while(valid)
1699 {
1700 int current_formid = -1;
1701 gtk_tree_model_get(model, iter, formid_col, &current_formid, -1);
1702 if(current_formid == formid) return TRUE;
1703 valid = gtk_tree_model_iter_next(model, iter);
1704 }
1705
1706 return FALSE;
1707}
1708
1709static void _blendop_masks_all_selection_changed(GtkTreeSelection *selection, dt_iop_module_t *module);
1710static gboolean _blendop_masks_all_button_pressed(GtkWidget *treeview, GdkEventButton *event,
1711 dt_iop_module_t *module);
1712static void _blendop_masks_group_selection_changed(GtkTreeSelection *selection, dt_iop_module_t *module);
1713static gboolean _blendop_masks_group_button_pressed(GtkWidget *treeview, GdkEventButton *event,
1714 dt_iop_module_t *module);
1715static void _blendop_masks_group_name_activate(GtkEntry *entry, dt_iop_module_t *module);
1716static gboolean _blendop_masks_group_name_focus_out(GtkWidget *widget, GdkEventFocus *event,
1717 dt_iop_module_t *module);
1718
1720{
1721 if(IS_NULL_PTR(module)) return;
1722 if(IS_NULL_PTR(module->blend_data)) return;
1724 if(IS_NULL_PTR(bd)) return;
1725 if(!GTK_IS_TREE_VIEW(bd->masks_treeview)) return;
1726 if(!GTK_IS_TREE_VIEW(bd->masks_group_treeview)) return;
1727
1728 GtkTreeModel *all_model = gtk_tree_view_get_model(GTK_TREE_VIEW(bd->masks_treeview));
1729 GtkTreeModel *group_model = gtk_tree_view_get_model(GTK_TREE_VIEW(bd->masks_group_treeview));
1730 if(!GTK_IS_LIST_STORE(all_model)) return;
1731 if(!GTK_IS_TREE_STORE(group_model)) return;
1732
1733 // Block signals during model updates to prevent callbacks from accessing inconsistent state
1734 GtkTreeSelection *all_selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(bd->masks_treeview));
1735 GtkTreeSelection *group_selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(bd->masks_group_treeview));
1736 g_signal_handlers_block_by_func(all_selection, G_CALLBACK(_blendop_masks_all_selection_changed), module);
1737 g_signal_handlers_block_by_func(group_selection, G_CALLBACK(_blendop_masks_group_selection_changed), module);
1738 g_signal_handlers_block_by_func(bd->masks_treeview, G_CALLBACK(_blendop_masks_all_button_pressed), module);
1739 g_signal_handlers_block_by_func(bd->masks_group_treeview, G_CALLBACK(_blendop_masks_group_button_pressed), module);
1740
1741 gtk_list_store_clear(GTK_LIST_STORE(all_model));
1742 gtk_tree_store_clear(GTK_TREE_STORE(group_model));
1743
1745
1746 // First pass: groups containing shapes first.
1747 for(const GList *form_node = darktable.develop->forms; form_node; form_node = g_list_next(form_node))
1748 {
1749 dt_masks_form_t *mask_form = (dt_masks_form_t *)form_node->data;
1750 if(IS_NULL_PTR(mask_form)) continue;
1751 if(mask_form->type & (DT_MASKS_CLONE | DT_MASKS_NON_CLONE)) continue;
1752 if(!IS_NULL_PTR(group_form) && mask_form->formid == group_form->formid) continue;
1753 if(!_blendop_masks_is_group_with_shapes(mask_form)) continue;
1754 // Skip groups containing only a single group
1755 if(_blendop_masks_is_single_group_wrapper(mask_form)) continue;
1756
1757 const gboolean active = _blendop_masks_find_group_entry(group_form, mask_form->formid, NULL) != NULL;
1758 GtkTreeIter iter;
1759 gtk_list_store_append(GTK_LIST_STORE(all_model), &iter);
1760 gchar *display_markup = g_markup_printf_escaped("%s", mask_form->name);
1761 gtk_list_store_set(GTK_LIST_STORE(all_model), &iter, BLENDOP_MASKS_ALL_COL_ACTIVE, active,
1764 BLENDOP_MASKS_ALL_COL_MARKUP, display_markup,
1766 -1);
1767 g_free(display_markup);
1768 }
1769
1770 // Second pass: then all non-group (and empty-group) entries.
1771 for(const GList *form_node = darktable.develop->forms; form_node; form_node = g_list_next(form_node))
1772 {
1773 dt_masks_form_t *mask_form = (dt_masks_form_t *)form_node->data;
1774 if(IS_NULL_PTR(mask_form)) continue;
1775 if(mask_form->type & (DT_MASKS_CLONE | DT_MASKS_NON_CLONE)) continue;
1776 if(!IS_NULL_PTR(group_form) && mask_form->formid == group_form->formid) continue;
1777 if(_blendop_masks_is_group_with_shapes(mask_form)) continue;
1778
1779 const gboolean active = _blendop_masks_find_group_entry(group_form, mask_form->formid, NULL) != NULL;
1780 gboolean sensitive = TRUE;
1781 const gchar *locked_group_name = NULL;
1782
1783 // Check if this shape belongs to an active group
1784 for(const GList *parent_node = darktable.develop->forms; parent_node; parent_node = g_list_next(parent_node))
1785 {
1786 dt_masks_form_t *parent_form = (dt_masks_form_t *)parent_node->data;
1787 if(IS_NULL_PTR(parent_form) || !_blendop_masks_is_group_with_shapes(parent_form)) continue;
1788
1789 const gboolean parent_active = _blendop_masks_find_group_entry(group_form, parent_form->formid, NULL) != NULL;
1790 if(parent_active && _blendop_masks_find_group_entry(parent_form, mask_form->formid, NULL))
1791 {
1792 sensitive = FALSE;
1793 locked_group_name = parent_form->name;
1794 break;
1795 }
1796 }
1797
1798 const gboolean display_active = active || !sensitive;
1799 gchar *display_markup = NULL;
1800 gchar *status_markup = NULL;
1801 display_markup = g_markup_printf_escaped("%s", mask_form->name);
1802
1803 if(!sensitive && !IS_NULL_PTR(locked_group_name) && *locked_group_name)
1804 {
1805 gchar *already_in = g_strdup_printf(_("Already in '%s'"), locked_group_name);
1806 status_markup = g_markup_printf_escaped("<i>%s</i>", already_in);
1807 g_free(already_in);
1808 }
1809 else
1810 status_markup = g_strdup("");
1811
1812 GtkTreeIter iter;
1813 gtk_list_store_append(GTK_LIST_STORE(all_model), &iter);
1814 gtk_list_store_set(GTK_LIST_STORE(all_model), &iter, BLENDOP_MASKS_ALL_COL_ACTIVE, display_active,
1818 -1);
1819 g_free(display_markup);
1820 g_free(status_markup);
1821 }
1822
1823 if(!IS_NULL_PTR(group_form) && (group_form->type & DT_MASKS_GROUP))
1824 {
1825 if(GTK_IS_ENTRY(bd->group_shapes_label))
1826 gtk_entry_set_text(GTK_ENTRY(bd->group_shapes_label), group_form->name);
1827 _blendop_masks_group_tree_append(bd, GTK_TREE_STORE(group_model), NULL, group_form);
1828 }
1829
1830 // Unblock signals after model updates are complete
1831 g_signal_handlers_unblock_by_func(all_selection, G_CALLBACK(_blendop_masks_all_selection_changed), module);
1832 g_signal_handlers_unblock_by_func(group_selection, G_CALLBACK(_blendop_masks_group_selection_changed), module);
1833 g_signal_handlers_unblock_by_func(bd->masks_treeview, G_CALLBACK(_blendop_masks_all_button_pressed), module);
1834 g_signal_handlers_unblock_by_func(bd->masks_group_treeview, G_CALLBACK(_blendop_masks_group_button_pressed), module);
1835}
1836
1838{
1839 if(IS_NULL_PTR(module)) return;
1840 if(IS_NULL_PTR(module->dev)) return;
1841
1842 dt_masks_iop_update(module);
1843 dt_dev_add_history_item(module->dev, module, TRUE, TRUE);
1846}
1847
1848static void _blendop_masks_group_name_commit(dt_iop_module_t *module, const gchar *new_text)
1849{
1850 if(IS_NULL_PTR(module)) return;
1852 if(IS_NULL_PTR(group_form)) return;
1853
1854 gchar *mask_default_name = dt_dev_get_masks_group_name(module);
1855 gchar *text = (new_text && *new_text) ? g_strdup(new_text) : g_strdup(mask_default_name);
1856 g_free(mask_default_name);
1857
1858 g_strlcpy(group_form->name, text, sizeof(group_form->name));
1860 g_free(text);
1861
1865}
1866
1867static void _blendop_masks_group_name_activate(GtkEntry *entry, dt_iop_module_t *module)
1868{
1869 if(!GTK_IS_ENTRY(entry)) return;
1870 _blendop_masks_group_name_commit(module, gtk_entry_get_text(entry));
1871}
1872
1873static gboolean _blendop_masks_group_name_focus_out(GtkWidget *widget, GdkEventFocus *event,
1874 dt_iop_module_t *module)
1875{
1876 (void)event;
1877 if(GTK_IS_ENTRY(widget))
1878 _blendop_masks_group_name_commit(module, gtk_entry_get_text(GTK_ENTRY(widget)));
1879 return FALSE;
1880}
1881
1882static void _blendop_masks_all_name_edited(GtkCellRendererText *cell, gchar *path_string,
1883 gchar *new_text, dt_iop_module_t *module)
1884{
1885 (void)cell;
1886 if(IS_NULL_PTR(module)) return;
1887 if(IS_NULL_PTR(module->blend_data)) return;
1888
1890 GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(bd->masks_treeview));
1891 if(!GTK_IS_TREE_MODEL(model)) return;
1892
1893 GtkTreeIter iter;
1894 if(!gtk_tree_model_get_iter_from_string(model, &iter, path_string)) return;
1895
1896 int formid = -1;
1897 gtk_tree_model_get(model, &iter, BLENDOP_MASKS_ALL_COL_FORMID, &formid, -1);
1899 if(IS_NULL_PTR(mask_form)) return;
1900
1901 const gchar *text = (new_text && *new_text) ? new_text : " ";
1902 g_strlcpy(mask_form->name, text, sizeof(mask_form->name));
1903
1908}
1909
1910static void _blendop_masks_all_selection_changed(GtkTreeSelection *selection, dt_iop_module_t *module)
1911{
1912 if(darktable.gui->reset) return;
1913 if(IS_NULL_PTR(module)) return;
1914 if(IS_NULL_PTR(selection)) return;
1915
1916 GtkTreeModel *model = NULL;
1917 GtkTreeIter iter;
1918 if(!gtk_tree_selection_get_selected(selection, &model, &iter)) return;
1919 if(!GTK_IS_TREE_MODEL(model)) return;
1920
1921 int formid = -1;
1922 gtk_tree_model_get(model, &iter, BLENDOP_MASKS_ALL_COL_FORMID, &formid, -1);
1923 if(formid <= 0) return;
1925 if(IS_NULL_PTR(mask_form)) return;
1926
1927 // Keep the global shape manager in sync without firing its selection handler.
1928 // That handler rebuilds the visible mask GUI and can be re-entered while this
1929 // blend list selection is still being processed.
1931 dt_masks_change_form_gui(mask_form);
1932 if(module->dev && module->dev->form_gui)
1933 {
1934 module->dev->form_gui->group_selected = 0;
1935 module->dev->form_gui->form_selected = TRUE;
1936 }
1937 dt_masks_center_view_on_form(module->dev, mask_form);
1938}
1939
1940static void _blendop_masks_all_toggled(GtkCellRendererToggle *cell, gchar *path_string, dt_iop_module_t *module)
1941{
1942 (void)cell;
1943 if(darktable.gui->reset) return;
1944 if(IS_NULL_PTR(module)) return;
1945 if(IS_NULL_PTR(module->blend_data)) return;
1946
1948 GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(bd->masks_treeview));
1949 if(!GTK_IS_TREE_MODEL(model)) return;
1950
1951 GtkTreeIter iter;
1952 if(!gtk_tree_model_get_iter_from_string(model, &iter, path_string)) return;
1953
1954 gboolean active = FALSE;
1955 gboolean sensitive = TRUE;
1956 int formid = -1;
1957 gtk_tree_model_get(model, &iter, BLENDOP_MASKS_ALL_COL_ACTIVE, &active,
1959 BLENDOP_MASKS_ALL_COL_SENSITIVE, &sensitive, -1);
1960
1961 if(!sensitive) return;
1962
1964 if(IS_NULL_PTR(mask_form)) return;
1965
1967 const int parentid_before = group_form ? group_form->formid : 0;
1968
1969 if(active)
1970 {
1971 if(!IS_NULL_PTR(group_form))
1972 dt_masks_form_delete(module, group_form, mask_form);
1973 }
1974 else
1975 {
1976 if(IS_NULL_PTR(group_form))
1977 group_form = _blendop_masks_group_create(module);
1978 if(IS_NULL_PTR(group_form)) return;
1979
1980 if(!_blendop_masks_find_group_entry(group_form, mask_form->formid, NULL))
1981 {
1982 dt_masks_group_add_form(group_form, mask_form);
1984 }
1985 }
1986
1987 // If this is a group with shapes, update sensitive state of child shapes
1989 {
1990 const gboolean new_active = !active; // Toggle state
1991 GtkTreeIter search_iter;
1992 gboolean valid = gtk_tree_model_get_iter_first(model, &search_iter);
1993
1994 while(valid)
1995 {
1996 int child_formid = -1;
1997 gtk_tree_model_get(model, &search_iter, BLENDOP_MASKS_ALL_COL_FORMID, &child_formid, -1);
1998
1999 // Check if this child belongs to the toggled group
2000 if(_blendop_masks_find_group_entry(mask_form, child_formid, NULL))
2001 {
2002 // Lock (gray out) if group is being activated, unlock if deactivated
2003 gtk_list_store_set(GTK_LIST_STORE(model), &search_iter,
2004 BLENDOP_MASKS_ALL_COL_SENSITIVE, !new_active, -1);
2005 }
2006
2007 valid = gtk_tree_model_iter_next(model, &search_iter);
2008 }
2009 }
2010
2012
2013 const int parentid_after = group_form ? group_form->formid : parentid_before;
2015 active ? parentid_before : parentid_after,
2017}
2018
2020{
2021 if(IS_NULL_PTR(module)) return;
2022
2023 const int formid = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(menu_item), "blend-formid"));
2025 if(IS_NULL_PTR(mask_form)) return;
2026
2028 dt_masks_form_delete(module, NULL, mask_form);
2031}
2032
2034{
2035 if(IS_NULL_PTR(module)) return;
2036
2037 const int formid = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(menu_item), "blend-formid"));
2038 if(dt_masks_form_duplicate(darktable.develop, formid) <= 0) return;
2039
2042}
2043
2045{
2046 if(IS_NULL_PTR(module)) return;
2047 if(IS_NULL_PTR(module->blend_data)) return;
2048
2049 const int formid = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(menu_item), "blend-formid"));
2051 GtkTreeView *view = GTK_TREE_VIEW(bd->masks_treeview);
2052 GtkTreeModel *model = gtk_tree_view_get_model(view);
2053 if(!GTK_IS_TREE_MODEL(model)) return;
2054
2055 GtkTreeIter iter;
2057
2058 GtkTreePath *path = gtk_tree_model_get_path(model, &iter);
2059 GtkTreeViewColumn *column = gtk_tree_view_get_column(view, 1);
2060 GtkCellRenderer *renderer = g_object_get_data(G_OBJECT(view), "blendop-masks-name-renderer");
2061 if(path && column && renderer)
2062 gtk_tree_view_set_cursor_on_cell(view, path, column, renderer, TRUE);
2063 if(path) gtk_tree_path_free(path);
2064}
2065
2066static gboolean _blendop_masks_all_handle_left_click(GtkWidget *treeview, GtkTreePath *path,
2067 GtkTreeViewColumn *column, dt_iop_module_t *module)
2068{
2069 if(IS_NULL_PTR(module)) return FALSE;
2070 if(IS_NULL_PTR(path) || IS_NULL_PTR(column)) return FALSE;
2071
2073 if(IS_NULL_PTR(bd) || column != bd->all_shapes_col) return FALSE;
2074
2075 GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(treeview));
2076 GtkTreeIter iter;
2077 if(!gtk_tree_model_get_iter(model, &iter, path)) return FALSE;
2078
2079 gboolean sensitive = TRUE;
2080 int formid = -1;
2081 gtk_tree_model_get(model, &iter, BLENDOP_MASKS_ALL_COL_SENSITIVE, &sensitive,
2082 BLENDOP_MASKS_ALL_COL_FORMID, &formid, -1);
2083
2084 if(formid <= 0) return FALSE;
2085
2086 if(!sensitive)
2087 {
2088 gtk_tree_path_free(path);
2089 return TRUE;
2090 }
2091
2092 // Toggle the checkbox value.
2093 gchar *path_string = gtk_tree_path_to_string(path);
2094 _blendop_masks_all_toggled(NULL, path_string, module);
2095 dt_free(path_string);
2096 gtk_tree_path_free(path);
2097 return TRUE; // Block default selection behavior.
2098}
2099
2100static gboolean _blendop_masks_all_button_pressed(GtkWidget *treeview, GdkEventButton *event,
2101 dt_iop_module_t *module)
2102{
2103 if(IS_NULL_PTR(event) || event->type != GDK_BUTTON_PRESS) return FALSE;
2104
2105 GtkTreePath *path = NULL;
2106 GtkTreeViewColumn *column = NULL;
2107 if(!gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(treeview), (gint)event->x, (gint)event->y, &path,
2108 &column, NULL, NULL))
2109 return FALSE;
2110
2111 // Handle left click on checkbox column - toggle directly without requiring selection
2112 if(event->button == GDK_BUTTON_PRIMARY && !IS_NULL_PTR(column))
2113 {
2114 if(_blendop_masks_all_handle_left_click(treeview, path, column, module))
2115 return TRUE;
2116 }
2117
2118 // Handle right click for context menu
2119 if(event->button != GDK_BUTTON_SECONDARY)
2120 {
2121 gtk_tree_path_free(path);
2122 return FALSE;
2123 }
2124
2125 GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview));
2126 gtk_tree_selection_unselect_all(selection);
2127 gtk_tree_selection_select_path(selection, path);
2128
2129 GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(treeview));
2130 GtkTreeIter iter;
2131 int formid = -1;
2132 if(gtk_tree_model_get_iter(model, &iter, path))
2133 gtk_tree_model_get(model, &iter, BLENDOP_MASKS_ALL_COL_FORMID, &formid, -1);
2134 gtk_tree_path_free(path);
2135 if(formid <= 0) return TRUE;
2136
2137 GtkWidget *menu = gtk_menu_new();
2138 GtkWidget *item = gtk_menu_item_new_with_label(_("Delete"));
2139 g_object_set_data(G_OBJECT(item), "blend-formid", GINT_TO_POINTER(formid));
2140 g_signal_connect(item, "activate", G_CALLBACK(_blendop_masks_all_delete_callback), module);
2141 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
2142
2143 item = gtk_menu_item_new_with_label(_("Duplicate"));
2144 g_object_set_data(G_OBJECT(item), "blend-formid", GINT_TO_POINTER(formid));
2145 g_signal_connect(item, "activate", G_CALLBACK(_blendop_masks_all_duplicate_callback), module);
2146 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
2147
2148 item = gtk_menu_item_new_with_label(_("Rename"));
2149 g_object_set_data(G_OBJECT(item), "blend-formid", GINT_TO_POINTER(formid));
2150 g_signal_connect(item, "activate", G_CALLBACK(_blendop_masks_all_rename_callback), module);
2151 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
2152
2153 gtk_widget_show_all(menu);
2154 gtk_menu_popup_at_pointer(GTK_MENU(menu), (GdkEvent *)event);
2155 return TRUE;
2156}
2157
2158static void _blendop_masks_group_operation_callback(GtkWidget *menu_item, gpointer user_data)
2159{
2160 dt_iop_module_t *module = (dt_iop_module_t *)user_data;
2161 if(IS_NULL_PTR(module)) return;
2162
2163 const int formid = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(menu_item), "blend-formid"));
2164 const int parentid = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(menu_item), "blend-parentid"));
2165 const dt_masks_state_t state = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(menu_item), "blend-state"));
2166
2167 dt_masks_form_group_t *group_entry = dt_masks_form_group_from_parentid(parentid, formid);
2168 if(IS_NULL_PTR(group_entry)) return;
2169
2170 const int old_state = group_entry->state;
2171 apply_operation(group_entry, state);
2172 if(group_entry->state == old_state) return;
2173
2177}
2178
2179static void _blendop_masks_group_selection_changed(GtkTreeSelection *selection, dt_iop_module_t *module)
2180{
2181 if(darktable.gui->reset) return;
2182 if(IS_NULL_PTR(module)) return;
2183 if(IS_NULL_PTR(selection)) return;
2184
2185 GtkTreeModel *model = NULL;
2186 GtkTreeIter iter;
2187 if(!gtk_tree_selection_get_selected(selection, &model, &iter)) return;
2188 if(!GTK_IS_TREE_MODEL(model)) return;
2189
2190 int formid = -1;
2191 gtk_tree_model_get(model, &iter, BLENDOP_MASKS_GROUP_COL_FORMID, &formid, -1);
2192 if(formid <= 0) return;
2194 if(IS_NULL_PTR(mask_form)) return;
2195
2196 // Switching edit mode rebuilds the visible module group. Do it only when the
2197 // current overlay does not already contain the selected row, because this
2198 // callback runs inside a GtkTreeSelection::changed emission.
2199 dt_masks_form_gui_t *gui = module->dev ? module->dev->form_gui : NULL;
2200 dt_masks_form_t *visible_form = module->dev ? dt_masks_get_visible_form(module->dev) : NULL;
2201 if(IS_NULL_PTR(gui) || gui->edit_mode != DT_MASKS_EDIT_FULL
2202 || dt_masks_group_index_from_formid(visible_form, formid) < 0)
2203 {
2204 // Rebuilding the visible group may update Gtk widgets. Hold the GUI reset
2205 // guard so selection callbacks emitted during that rebuild don't recurse.
2206 ++darktable.gui->reset;
2208 --darktable.gui->reset;
2209 }
2210
2211 // Keep lib/masks tree selection in sync without re-triggering its selection handler,
2212 // otherwise the visible overlay can be replaced by only the clicked shape.
2214
2215 // Mark the selected shape as active in the central mask GUI state.
2216 if(module->dev && module->dev->form_gui)
2217 {
2218 gui = module->dev->form_gui;
2219 visible_form = dt_masks_get_visible_form(module->dev);
2220 const int selected_index = dt_masks_group_index_from_formid(visible_form, formid);
2221 if(selected_index >= 0)
2222 {
2223 gui->group_selected = selected_index;
2224 gui->form_selected = TRUE;
2225 gui->border_selected = FALSE;
2226 gui->source_selected = FALSE;
2227 gui->node_selected = FALSE;
2228 gui->handle_selected = FALSE;
2229 gui->seg_selected = FALSE;
2231 gui->node_selected_idx = -1;
2232 gui->form_dragging = FALSE;
2233 gui->source_dragging = FALSE;
2234 gui->form_rotating = FALSE;
2235 gui->pivot_selected = FALSE;
2236 }
2237 }
2238 dt_masks_center_view_on_form(module->dev, mask_form);
2239}
2240
2241static gboolean _blendop_masks_group_move_by_index(dt_masks_form_t *group_form, const int index,
2242 const gboolean move_up)
2243{
2244 if(IS_NULL_PTR(group_form)) return FALSE;
2245 if(!(group_form->type & DT_MASKS_GROUP)) return FALSE;
2246 if(index < 0) return FALSE;
2247
2248 const guint length = g_list_length(group_form->points);
2249 if(length == 0 || (guint)index >= length) return FALSE;
2250 if(move_up && index == 0) return FALSE;
2251 if(!move_up && (guint)index >= length - 1) return FALSE;
2252
2253 dt_masks_form_group_t *entry = (dt_masks_form_group_t *)g_list_nth_data(group_form->points, index);
2254 if(IS_NULL_PTR(entry)) return FALSE;
2255
2256 const int new_index = move_up ? index - 1 : index + 1;
2257 group_form->points = g_list_remove(group_form->points, entry);
2258 group_form->points = g_list_insert(group_form->points, entry, new_index);
2259 return TRUE;
2260}
2261
2263{
2264 if(IS_NULL_PTR(module)) return;
2265
2266 const int parentid = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(menu_item), "blend-parentid"));
2267 const int index = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(menu_item), "blend-index"));
2268 const gboolean move_up = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(menu_item), "blend-move-up"));
2269 dt_masks_form_t *group_form = dt_masks_get_from_id(darktable.develop, parentid);
2270 if(!_blendop_masks_group_move_by_index(group_form, index, move_up)) return;
2271
2275}
2276
2277static void _blendop_masks_edit_list_toggle(GtkToggleButton *togglebutton, dt_iop_module_t *module)
2278{
2280
2281 if(!GTK_IS_TOGGLE_BUTTON(togglebutton)) return;
2282
2283 // The edit toggle swaps the visible list between:
2284 // - current module mask tree
2285 // - global all-shapes list
2286 const gboolean edit_mode = gtk_toggle_button_get_active(togglebutton);
2287
2288 if(edit_mode)
2289 gtk_button_set_label(GTK_BUTTON(togglebutton), _("OK"));
2290 else
2291 gtk_button_set_label(GTK_BUTTON(togglebutton), _("Attach shapes"));
2292
2293 if(GTK_IS_STACK(bd->lists_stack))
2294 gtk_stack_set_visible_child_name(GTK_STACK(bd->lists_stack), edit_mode ? "all" : "group");
2295
2296 if(GTK_IS_ENTRY(bd->group_shapes_label))
2297 gtk_widget_set_sensitive(bd->group_shapes_label, !edit_mode);
2298}
2299
2301{
2302 if(IS_NULL_PTR(module) || IS_NULL_PTR(bd)) return NULL;
2303
2304 const dt_masks_shape_buttons_config_t config = {
2305 .owner_module = module,
2306 .creation_module = module,
2307 .buttons = bd->masks_shapes,
2308 .types = bd->masks_type,
2309 .action_section = "blend_shapes",
2311 .register_flags = DT_MASKS_SHAPE_BUTTONS_POLYGON,
2312 .local = FALSE,
2313 .user_data = bd,
2314 .can_start = _blendop_masks_shape_can_start,
2315 .form_type = NULL,
2316 .started = NULL,
2317 .exited = NULL
2318 };
2319 GtkWidget *widget = dt_masks_shape_buttons_create(&config);
2320
2321 return widget;
2322}
2323
2325 const int formid, const int parentid, const int state,
2326 const int index, const int list_length)
2327{
2328 if(IS_NULL_PTR(bd) || IS_NULL_PTR(module)) return NULL;
2329
2330 // Initialize mask operation icons if needed
2332
2333 GtkWidget *menu = gtk_menu_new();
2334 gtk_style_context_add_class(gtk_widget_get_style_context(menu), "dt-masks-context-menu");
2335
2336 GtkWidget *op_item = gtk_menu_item_new_with_label(_("Operation"));
2337 GtkWidget *op_submenu = gtk_menu_new();
2338 gtk_menu_item_set_submenu(GTK_MENU_ITEM(op_item), op_submenu);
2339 gtk_menu_shell_append(GTK_MENU_SHELL(menu), op_item);
2340
2342 op_submenu,
2344 module,
2346 g_object_set_data(G_OBJECT(item), "blend-formid", GINT_TO_POINTER(formid));
2347 g_object_set_data(G_OBJECT(item), "blend-parentid", GINT_TO_POINTER(parentid));
2348 g_object_set_data(G_OBJECT(item), "blend-state", GINT_TO_POINTER(DT_MASKS_STATE_INVERSE));
2349
2350 gtk_menu_shell_append(GTK_MENU_SHELL(op_submenu), gtk_separator_menu_item_new());
2351
2352 item = ctx_gtk_check_menu_item_new_with_markup_and_pixbuf(_("Union"), bd->masks_ic_union, op_submenu,
2354 (state & DT_MASKS_STATE_UNION) != 0, FALSE);
2355 gtk_widget_set_sensitive(item, index > 0);
2356 g_object_set_data(G_OBJECT(item), "blend-formid", GINT_TO_POINTER(formid));
2357 g_object_set_data(G_OBJECT(item), "blend-parentid", GINT_TO_POINTER(parentid));
2358 g_object_set_data(G_OBJECT(item), "blend-state", GINT_TO_POINTER(DT_MASKS_STATE_UNION));
2359
2360 item = ctx_gtk_check_menu_item_new_with_markup_and_pixbuf(_("Intersection"), bd->masks_ic_intersection, op_submenu,
2363 gtk_widget_set_sensitive(item, index > 0);
2364 g_object_set_data(G_OBJECT(item), "blend-formid", GINT_TO_POINTER(formid));
2365 g_object_set_data(G_OBJECT(item), "blend-parentid", GINT_TO_POINTER(parentid));
2366 g_object_set_data(G_OBJECT(item), "blend-state", GINT_TO_POINTER(DT_MASKS_STATE_INTERSECTION));
2367
2368 item = ctx_gtk_check_menu_item_new_with_markup_and_pixbuf(_("Difference"), bd->masks_ic_difference, op_submenu,
2371 gtk_widget_set_sensitive(item, index > 0);
2372 g_object_set_data(G_OBJECT(item), "blend-formid", GINT_TO_POINTER(formid));
2373 g_object_set_data(G_OBJECT(item), "blend-parentid", GINT_TO_POINTER(parentid));
2374 g_object_set_data(G_OBJECT(item), "blend-state", GINT_TO_POINTER(DT_MASKS_STATE_DIFFERENCE));
2375
2376 item = ctx_gtk_check_menu_item_new_with_markup_and_pixbuf(_("Exclusion"), bd->masks_ic_exclusion, op_submenu,
2379 gtk_widget_set_sensitive(item, index > 0);
2380 g_object_set_data(G_OBJECT(item), "blend-formid", GINT_TO_POINTER(formid));
2381 g_object_set_data(G_OBJECT(item), "blend-parentid", GINT_TO_POINTER(parentid));
2382 g_object_set_data(G_OBJECT(item), "blend-state", GINT_TO_POINTER(DT_MASKS_STATE_EXCLUSION));
2383
2384 gtk_menu_shell_append(GTK_MENU_SHELL(menu), gtk_separator_menu_item_new());
2385
2386 item = gtk_menu_item_new_with_label(_("Move Up"));
2387 gtk_widget_set_sensitive(item, index > 0);
2388 g_object_set_data(G_OBJECT(item), "blend-parentid", GINT_TO_POINTER(parentid));
2389 g_object_set_data(G_OBJECT(item), "blend-index", GINT_TO_POINTER(index));
2390 g_object_set_data(G_OBJECT(item), "blend-move-up", GINT_TO_POINTER(TRUE));
2391 g_signal_connect(item, "activate", G_CALLBACK(_blendop_masks_group_move_callback), module);
2392 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
2393
2394 item = gtk_menu_item_new_with_label(_("Move Down"));
2395 gtk_widget_set_sensitive(item, index >= 0 && index < list_length - 1);
2396 g_object_set_data(G_OBJECT(item), "blend-parentid", GINT_TO_POINTER(parentid));
2397 g_object_set_data(G_OBJECT(item), "blend-index", GINT_TO_POINTER(index));
2398 g_object_set_data(G_OBJECT(item), "blend-move-up", GINT_TO_POINTER(FALSE));
2399 g_signal_connect(item, "activate", G_CALLBACK(_blendop_masks_group_move_callback), module);
2400 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
2401
2402 return menu;
2403}
2404
2405// Detach the form from its parent group, but keep it in dev->forms for potential reuse.
2406static void _blendop_masks_group_unlink(dt_iop_module_t *module, const int formid, const int parentid)
2407{
2408 if(IS_NULL_PTR(module)) return;
2409 dt_masks_form_t *parent_group = dt_masks_get_from_id(darktable.develop, parentid);
2411 if(IS_NULL_PTR(parent_group) || !(parent_group->type & DT_MASKS_GROUP) || IS_NULL_PTR(form)) return;
2412
2413 // Discard any visible overlay before mutating the group.
2415
2416 // Passing the parent group only detaches the form from this group.
2417 dt_masks_form_delete(module, parent_group, form);
2418
2423}
2424
2425static gboolean _blendop_masks_confirm_delete(const char *form_name)
2426{
2428
2429 GtkWidget *dialog = gtk_message_dialog_new(GTK_WINDOW(dt_ui_main_window(darktable.gui->ui)),
2430 GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_MODAL,
2431 GTK_MESSAGE_QUESTION, GTK_BUTTONS_NONE,
2432 _("Permanently delete the shape '%s'?"), form_name);
2433 gtk_message_dialog_format_secondary_text(
2434 GTK_MESSAGE_DIALOG(dialog), "%s",
2435 _("It will be detached from this mask and removed from the list of available shapes."));
2436 gtk_dialog_add_button(GTK_DIALOG(dialog), _("Cancel"), GTK_RESPONSE_CANCEL);
2437 gtk_dialog_add_button(GTK_DIALOG(dialog), _("Delete"), GTK_RESPONSE_YES);
2438 gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_CANCEL);
2439
2440 const int response = gtk_dialog_run(GTK_DIALOG(dialog));
2441 gtk_widget_destroy(dialog);
2442 return response == GTK_RESPONSE_YES;
2443}
2444
2445// Detach the form from its parent group and remove it from dev->forms altogether.
2446static void _blendop_masks_group_delete(dt_iop_module_t *module, const int formid, const int parentid)
2447{
2448 if(IS_NULL_PTR(module)) return;
2450 if(IS_NULL_PTR(form)) return;
2451
2452 if(!_blendop_masks_confirm_delete(form->name)) return;
2453
2454 // Discard any visible overlay before mutating the masks.
2456
2457 // Passing no group permanently deletes the form from every module and dev->forms.
2458 dt_masks_form_delete(module, NULL, form);
2459
2464}
2465
2466// Handle a left click on the per-row unlink / delete icon columns.
2467// Returns TRUE if the click was consumed (so row selection is not triggered).
2468static gboolean _blendop_masks_group_handle_action_click(GtkWidget *treeview, GtkTreePath *path,
2469 GtkTreeViewColumn *column, dt_iop_module_t *module)
2470{
2471 if(IS_NULL_PTR(module) || IS_NULL_PTR(path) || IS_NULL_PTR(column)) return FALSE;
2473 if(IS_NULL_PTR(bd)) return FALSE;
2474 if(column != bd->group_unlink_col && column != bd->group_delete_col) return FALSE;
2475
2476 GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(treeview));
2477 GtkTreeIter iter;
2478 if(!gtk_tree_model_get_iter(model, &iter, path)) return TRUE;
2479
2480 int formid = -1;
2481 int parentid = -1;
2482 gtk_tree_model_get(model, &iter, BLENDOP_MASKS_GROUP_COL_FORMID, &formid,
2483 BLENDOP_MASKS_GROUP_COL_PARENTID, &parentid, -1);
2484 if(formid <= 0 || parentid <= 0) return TRUE;
2485
2486 if(column == bd->group_unlink_col)
2487 _blendop_masks_group_unlink(module, formid, parentid);
2488 else
2489 _blendop_masks_group_delete(module, formid, parentid);
2490
2491 return TRUE;
2492}
2493
2494static gboolean _blendop_masks_group_button_pressed(GtkWidget *treeview, GdkEventButton *event,
2495 dt_iop_module_t *module)
2496{
2497 if(IS_NULL_PTR(event) || event->type != GDK_BUTTON_PRESS) return FALSE;
2498
2499 GtkTreePath *path = NULL;
2500 GtkTreeViewColumn *column = NULL;
2501 if(!gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(treeview), (gint)event->x, (gint)event->y, &path,
2502 &column, NULL, NULL))
2503 return FALSE;
2504
2505 // Left click on the unlink / delete icon columns triggers the row action.
2506 if(event->button == GDK_BUTTON_PRIMARY)
2507 {
2508 const gboolean consumed = _blendop_masks_group_handle_action_click(treeview, path, column, module);
2509 gtk_tree_path_free(path);
2510 return consumed;
2511 }
2512
2513 if(event->button != GDK_BUTTON_SECONDARY)
2514 {
2515 gtk_tree_path_free(path);
2516 return FALSE;
2517 }
2518
2519 GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview));
2520 gtk_tree_selection_unselect_all(selection);
2521 gtk_tree_selection_select_path(selection, path);
2522
2523 GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(treeview));
2524 GtkTreeIter iter;
2525 int formid = -1;
2526 int parentid = -1;
2527 int state = 0;
2528 int index = -1;
2529 if(gtk_tree_model_get_iter(model, &iter, path))
2530 gtk_tree_model_get(model, &iter, BLENDOP_MASKS_GROUP_COL_FORMID, &formid,
2533 BLENDOP_MASKS_GROUP_COL_INDEX, &index, -1);
2534 gtk_tree_path_free(path);
2535
2536 if(formid <= 0 || parentid <= 0) return TRUE;
2537
2539 dt_masks_form_t *parent_group = dt_masks_get_from_id(darktable.develop, parentid);
2540 const int list_length = (parent_group && (parent_group->type & DT_MASKS_GROUP))
2541 ? g_list_length(parent_group->points)
2542 : 0;
2543
2544 GtkWidget *menu = _blendop_masks_group_ctx_menu(bd, module, formid, parentid, state, index, list_length);
2545 if(IS_NULL_PTR(menu)) return TRUE;
2546 gtk_widget_show_all(menu);
2547 gtk_menu_popup_at_pointer(GTK_MENU(menu), (GdkEvent *)event);
2548 return TRUE;
2549}
2550
2551// Per-column tooltips for the unlink / delete action icons.
2552static gboolean _blendop_masks_group_query_tooltip(GtkWidget *treeview, gint x, gint y, gboolean keyboard_tip,
2553 GtkTooltip *tooltip, dt_iop_module_t *module)
2554{
2555 if(IS_NULL_PTR(module) || keyboard_tip) return FALSE;
2557 if(IS_NULL_PTR(bd)) return FALSE;
2558
2559 gint bx = 0, by = 0;
2560 gtk_tree_view_convert_widget_to_bin_window_coords(GTK_TREE_VIEW(treeview), x, y, &bx, &by);
2561
2562 GtkTreePath *path = NULL;
2563 GtkTreeViewColumn *column = NULL;
2564 if(!gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(treeview), bx, by, &path, &column, NULL, NULL))
2565 return FALSE;
2566 gtk_tree_path_free(path);
2567
2568 const char *text = NULL;
2569 if(column == bd->group_unlink_col)
2570 text = _("Detach this shape from the mask. The shape is kept and stays available for reuse.");
2571 else if(column == bd->group_delete_col)
2572 text = _("Permanently delete this shape. It is detached from the mask and removed from the list of available shapes.");
2573
2574 if(IS_NULL_PTR(text)) return FALSE;
2575
2576 gtk_tooltip_set_text(tooltip, text);
2577 return TRUE;
2578}
2579
2580// Depth-first search for the row matching (formid, parentid) anywhere in the group tree.
2581static gboolean _blendop_masks_group_find_row(GtkTreeModel *model, GtkTreeIter *parent,
2582 const int formid, const int parentid, GtkTreeIter *out)
2583{
2584 GtkTreeIter iter;
2585 gboolean valid = gtk_tree_model_iter_children(model, &iter, parent);
2586 while(valid)
2587 {
2588 int fid = -1;
2589 int pid = -1;
2590 gtk_tree_model_get(model, &iter, BLENDOP_MASKS_GROUP_COL_FORMID, &fid,
2592 if(fid == formid && pid == parentid)
2593 {
2594 *out = iter;
2595 return TRUE;
2596 }
2597 if(gtk_tree_model_iter_has_child(model, &iter)
2598 && _blendop_masks_group_find_row(model, &iter, formid, parentid, out))
2599 return TRUE;
2600 valid = gtk_tree_model_iter_next(model, &iter);
2601 }
2602 return FALSE;
2603}
2604
2605// Refresh a single row in place (name + opacity + operation/inverse icons), the same way
2606// the masks library treeview does, so live opacity changes don't rebuild and collapse the tree.
2607static void _blendop_masks_group_update_row(dt_iop_module_t *module, const int formid, const int parentid)
2608{
2609 if(IS_NULL_PTR(module) || IS_NULL_PTR(module->blend_data)) return;
2611 if(!GTK_IS_TREE_VIEW(bd->masks_group_treeview)) return;
2612 GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(bd->masks_group_treeview));
2613 if(!GTK_IS_TREE_STORE(model)) return;
2614
2615 GtkTreeIter iter;
2616 if(!_blendop_masks_group_find_row(model, NULL, formid, parentid, &iter)) return;
2617
2619 dt_masks_form_t *parent_group = dt_masks_get_from_id(darktable.develop, parentid);
2620 int index = -1;
2621 dt_masks_form_group_t *entry = _blendop_masks_find_group_entry(parent_group, formid, &index);
2622 if(IS_NULL_PTR(form) || IS_NULL_PTR(entry)) return;
2623
2624 char display_name[256] = "";
2625 if(entry->opacity != 1.0f)
2626 g_snprintf(display_name, sizeof(display_name), "%s %d%%", form->name, (int)(entry->opacity * 100));
2627 else
2628 g_strlcpy(display_name, form->name, sizeof(display_name));
2629
2630 gtk_tree_store_set(GTK_TREE_STORE(model), &iter, BLENDOP_MASKS_GROUP_COL_NAME, display_name,
2634}
2635
2636static void _blendop_masks_handler_callback(gpointer instance, const int formid, const int parentid,
2637 const dt_masks_event_t event, dt_iop_module_t *module)
2638{
2639 (void)instance;
2640 // A plain value update (e.g. opacity) only needs the affected row refreshed in place.
2641 // Structural changes rebuild the whole list.
2642 if(event == DT_MASKS_EVENT_UPDATE)
2643 _blendop_masks_group_update_row(module, formid, parentid);
2644 else
2646}
2647
2650{
2651 dt_iop_gui_blend_data_t *data = module->blend_data;
2652
2653 if(picker == data->colorpicker_set_values)
2654 {
2655 if(darktable.gui->reset) return TRUE;
2656
2657 ++darktable.gui->reset;
2658
2659 dt_develop_blend_params_t *bp = module->blend_params;
2660
2661 const int tab = data->tab;
2662 dt_aligned_pixel_t raw_min, raw_max;
2663 float picker_min[8] DT_ALIGNED_PIXEL, picker_max[8] DT_ALIGNED_PIXEL;
2664 dt_aligned_pixel_t picker_values;
2665
2666 const int in_out = ((dt_key_modifier_state() == GDK_CONTROL_MASK) && data->output_channels_shown) ? 1 : 0;
2667
2668 if(in_out)
2669 {
2670 for(size_t i = 0; i < 4; i++)
2671 {
2672 raw_min[i] = module->picked_output_color_min[i];
2673 raw_max[i] = module->picked_output_color_max[i];
2674 }
2675 }
2676 else
2677 {
2678 for(size_t i = 0; i < 4; i++)
2679 {
2680 raw_min[i] = module->picked_color_min[i];
2681 raw_max[i] = module->picked_color_max[i];
2682 }
2683 }
2684
2685 const dt_iop_gui_blendif_channel_t *channel = &data->channel[data->tab];
2687 dt_iop_gui_blendif_filter_t *sl = &data->filter[in_out];
2688
2689 float *parameters = &(bp->blendif_parameters[4 * ch]);
2690
2691 const dt_develop_blend_colorspace_t blend_csp = data->channel_tabs_csp;
2693 const dt_iop_order_iccprofile_info_t *work_profile = (blend_csp == DEVELOP_BLEND_CS_RGB_SCENE)
2694 ? dt_ioppr_get_pipe_current_profile_info(piece->module, pipe)
2695 : dt_ioppr_get_iop_work_profile_info(module, module->dev->iop);
2696
2697 gboolean reverse_hues = FALSE;
2698 if(cst == IOP_CS_HSL && tab == CHANNEL_INDEX_H)
2699 {
2700 if((raw_max[3] - raw_min[3]) < (raw_max[0] - raw_min[0]) && raw_min[3] < 0.5f && raw_max[3] > 0.5f)
2701 {
2702 raw_max[0] = raw_max[3] < 0.5f ? raw_max[3] + 0.5f : raw_max[3] - 0.5f;
2703 raw_min[0] = raw_min[3] < 0.5f ? raw_min[3] + 0.5f : raw_min[3] - 0.5f;
2704 reverse_hues = TRUE;
2705 }
2706 }
2707 else if((cst == IOP_CS_LCH && tab == CHANNEL_INDEX_h) || (cst == IOP_CS_JZCZHZ && tab == CHANNEL_INDEX_hz))
2708 {
2709 if((raw_max[3] - raw_min[3]) < (raw_max[2] - raw_min[2]) && raw_min[3] < 0.5f && raw_max[3] > 0.5f)
2710 {
2711 raw_max[2] = raw_max[3] < 0.5f ? raw_max[3] + 0.5f : raw_max[3] - 0.5f;
2712 raw_min[2] = raw_min[3] < 0.5f ? raw_min[3] + 0.5f : raw_min[3] - 0.5f;
2713 reverse_hues = TRUE;
2714 }
2715 }
2716
2717 _blendif_scale(data, cst, raw_max, picker_max, work_profile, in_out);
2718
2719 gboolean picker_box_changed = FALSE;
2720 const dt_colorpicker_sample_t *const sample = module->dev->color_picker.primary_sample;
2721 if(!IS_NULL_PTR(sample) && sample->size == DT_LIB_COLORPICKER_SIZE_BOX)
2722 {
2724 {
2725 picker_box_changed = TRUE;
2726 }
2727 else
2728 {
2729 const float epsilon = 1e-6f;
2730 for(int k = 0; k < 4; k++)
2731 {
2732 if(fabsf(sample->box[k] - data->picker_set_values_box[k]) > epsilon)
2733 {
2734 picker_box_changed = TRUE;
2735 break;
2736 }
2737 }
2738 }
2739
2740 if(picker_box_changed)
2741 {
2742 for(int k = 0; k < 4; k++) data->picker_set_values_box[k] = sample->box[k];
2745 }
2746 }
2747 else
2748 {
2751 }
2752
2764 if(channel->boost_factor_enabled && picker_box_changed
2766 {
2767 const float picker_margin = 0.02f;
2768 const float boost_step = 0.25f;
2769 const int max_boost_iters = 64;
2770 const float boost_min = channel->boost_factor_offset;
2771 const float boost_max = channel->boost_factor_offset + 18.0f;
2772 const float target_max = (picker_max[tab] > (1.0f - picker_margin)) ? 1.0f : (1.0f - picker_margin);
2773 float trial = bp->blendif_boost_factors[ch];
2774 gboolean within_bounds = picker_max[tab] <= target_max;
2775
2776 if(!within_bounds)
2777 {
2778 for(int iter = 0; iter < max_boost_iters && trial < boost_max; iter++)
2779 {
2780 trial = fminf(trial + boost_step, boost_max);
2781 bp->blendif_boost_factors[ch] = trial;
2782 _blendif_scale(data, cst, raw_max, picker_max, work_profile, in_out);
2783 within_bounds = picker_max[tab] <= target_max;
2784 if(within_bounds) break;
2785 }
2786 }
2787
2788 if(within_bounds)
2789 {
2790 for(int iter = 0; iter < max_boost_iters && trial > boost_min; iter++)
2791 {
2792 const float candidate = fmaxf(trial - boost_step, boost_min);
2793 bp->blendif_boost_factors[ch] = candidate;
2794 _blendif_scale(data, cst, raw_max, picker_max, work_profile, in_out);
2795 if(picker_max[tab] > target_max)
2796 break;
2797 trial = candidate;
2798 }
2799 }
2800
2801 bp->blendif_boost_factors[ch] = trial;
2802 _blendif_scale(data, cst, raw_max, picker_max, work_profile, in_out);
2803 }
2804
2805 _blendif_scale(data, cst, raw_min, picker_min, work_profile, in_out);
2806
2807 const float feather = 0.01f;
2808
2809 if(picker_min[tab] > picker_max[tab])
2810 {
2811 const float tmp = picker_min[tab];
2812 picker_min[tab] = picker_max[tab];
2813 picker_max[tab] = tmp;
2814 }
2815
2816 picker_values[0] = CLAMP(picker_min[tab] - feather, 0.f, 1.f);
2817 picker_values[1] = CLAMP(picker_min[tab] + feather, 0.f, 1.f);
2818 picker_values[2] = CLAMP(picker_max[tab] - feather, 0.f, 1.f);
2819 picker_values[3] = CLAMP(picker_max[tab] + feather, 0.f, 1.f);
2820
2821 if(picker_values[1] > picker_values[2])
2822 {
2823 picker_values[1] = CLAMP(picker_min[tab], 0.f, 1.f);
2824 picker_values[2] = CLAMP(picker_max[tab], 0.f, 1.f);
2825 }
2826
2827 picker_values[0] = CLAMP(picker_values[0], 0.f, picker_values[1]);
2828 picker_values[3] = CLAMP(picker_values[3], picker_values[2], 1.f);
2829
2831 for(int k = 0; k < 4; k++)
2834
2835 // update picked values
2836 _update_gradient_slider_pickers(NULL, module);
2837
2838 const float boost_factor = _get_boost_factor(data, data->tab, in_out);
2839 for(int k = 0; k < 4; k++)
2840 {
2841 char text[256];
2843 text, sizeof(text));
2844 gtk_label_set_text(sl->label[k], text);
2845 }
2846
2847 --darktable.gui->reset;
2848
2849 // save values to parameters
2851 for(int k = 0; k < 4; k++)
2854
2855 // de-activate processing of this channel if maximum span is selected
2856 if(parameters[1] == 0.0f && parameters[2] == 1.0f)
2857 bp->blendif &= ~(1 << ch);
2858 else
2859 bp->blendif |= (1 << ch);
2860
2861 // set the polarity of the channel to include the picked values
2862 if(reverse_hues == ((bp->mask_combine & DEVELOP_COMBINE_INV) == DEVELOP_COMBINE_INV))
2863 bp->blendif &= ~(1 << (16 + ch));
2864 else
2865 bp->blendif |= 1 << (16 + ch);
2866
2868
2870 _blendop_blendif_update_tab(module, tab);
2871
2872 return TRUE;
2873 }
2874 else if(picker == data->colorpicker)
2875 {
2876 if(darktable.gui->reset) return TRUE;
2877
2878 _update_gradient_slider_pickers(NULL, module);
2879
2880 return TRUE;
2881 }
2882 else return FALSE; // needs to be handled by module
2883}
2884
2886{
2887 switch(cst)
2888 {
2893 break;
2894 default:
2896 break;
2897 }
2898 if(cst != module->blend_params->blend_cst)
2899 {
2901
2902 // look for last history item for this module with the selected blending mode to copy parametric mask settings
2903 for(const GList *history = g_list_last(darktable.develop->history); history; history = g_list_previous(history))
2904 {
2905 const dt_dev_history_item_t *data = (dt_dev_history_item_t *)(history->data);
2906 if(data->module == module && data->blend_params->blend_cst == cst)
2907 {
2908 const dt_develop_blend_params_t *hp = data->blend_params;
2909 dt_develop_blend_params_t *np = module->blend_params;
2910
2911 np->blend_mode = hp->blend_mode;
2913 np->blendif = hp->blendif;
2914 memcpy(np->blendif_parameters, hp->blendif_parameters, sizeof(hp->blendif_parameters));
2916 break;
2917 }
2918 }
2919
2920 dt_iop_gui_blend_data_t *bd = module->blend_data;
2921 const int cst_old = _blendop_blendif_get_picker_colorspace(bd);
2923 dt_iop_gui_update(module);
2924
2925 if(cst_old != _blendop_blendif_get_picker_colorspace(bd) &&
2926 (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(bd->colorpicker)) ||
2927 gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(bd->colorpicker_set_values))))
2928 {
2930 dt_dev_pixelpipe_update_history_all(bd->module->dev);
2931 }
2932
2933 return TRUE;
2934 }
2935 return FALSE;
2936}
2937
2938static void _blendif_select_colorspace(GtkMenuItem *menuitem, dt_iop_module_t *module)
2939{
2940 dt_develop_blend_colorspace_t cst = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(menuitem), "dt-blend-cst"));
2941 if(_blendif_change_blend_colorspace(module, cst))
2942 {
2943 gtk_widget_queue_draw(module->widget);
2944 }
2945}
2946
2947static void _blendif_show_output_channels(GtkMenuItem *menuitem, dt_iop_module_t *module)
2948{
2950 if(IS_NULL_PTR(bd) || !bd->blendif_support || !bd->blendif_inited) return;
2951 if(!bd->output_channels_shown)
2952 {
2954 dt_iop_gui_update(module);
2955 }
2956}
2957
2958static void _blendif_hide_output_channels(GtkMenuItem *menuitem, dt_iop_module_t *module)
2959{
2961 if(IS_NULL_PTR(bd) || !bd->blendif_support || !bd->blendif_inited) return;
2962 if(bd->output_channels_shown)
2963 {
2966 {
2968 }
2969 dt_iop_gui_update(module);
2970 }
2971}
2972
2973static void _blendif_options_callback(GtkButton *button, GdkEventButton *event, dt_iop_module_t *module)
2974{
2975 if(event->button != 1 && event->button != 2) return;
2977 if(IS_NULL_PTR(bd) || !bd->blendif_support || !bd->blendif_inited) return;
2978
2979 GtkWidget *mi;
2980 GtkMenu *menu = darktable.gui->presets_popup_menu;
2981 if(menu) gtk_widget_destroy(GTK_WIDGET(menu));
2982 darktable.gui->presets_popup_menu = GTK_MENU(gtk_menu_new());
2984
2985 // add a section to switch blending color spaces
2987 const dt_develop_blend_colorspace_t module_blend_cst = module->blend_params->blend_cst;
2988 if(module_cst == DEVELOP_BLEND_CS_LAB || module_cst == DEVELOP_BLEND_CS_RGB_DISPLAY
2989 || module_cst == DEVELOP_BLEND_CS_RGB_SCENE)
2990 {
2991
2992 mi = gtk_menu_item_new_with_label(_("reset to default blend colorspace"));
2993 g_object_set_data_full(G_OBJECT(mi), "dt-blend-cst", GINT_TO_POINTER(DEVELOP_BLEND_CS_NONE), NULL);
2994 g_signal_connect(G_OBJECT(mi), "activate", G_CALLBACK(_blendif_select_colorspace), module);
2995 gtk_menu_shell_append(GTK_MENU_SHELL(menu), mi);
2996
2997 // only show Lab blending when the module is a Lab module to avoid using it at the wrong place (Lab blending
2998 // should not be activated for RGB modules before colorin and after colorout)
2999 if(module_cst == DEVELOP_BLEND_CS_LAB)
3000 {
3001 mi = gtk_check_menu_item_new_with_label(_("Lab"));
3002
3003 if(module_blend_cst == DEVELOP_BLEND_CS_LAB)
3004 {
3005 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(mi), TRUE);
3006 dt_gui_add_class(mi, "active_menu_item");
3007 }
3008 g_object_set_data_full(G_OBJECT(mi), "dt-blend-cst", GINT_TO_POINTER(DEVELOP_BLEND_CS_LAB), NULL);
3009 g_signal_connect(G_OBJECT(mi), "activate", G_CALLBACK(_blendif_select_colorspace), module);
3010 gtk_menu_shell_append(GTK_MENU_SHELL(menu), mi);
3011 }
3012
3013 mi = gtk_check_menu_item_new_with_label(_("RGB (display)"));
3014
3015 if(module_blend_cst == DEVELOP_BLEND_CS_RGB_DISPLAY)
3016 {
3017 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(mi), TRUE);
3018 dt_gui_add_class(mi, "active_menu_item");
3019 }
3020 g_object_set_data_full(G_OBJECT(mi), "dt-blend-cst", GINT_TO_POINTER(DEVELOP_BLEND_CS_RGB_DISPLAY), NULL);
3021 g_signal_connect(G_OBJECT(mi), "activate", G_CALLBACK(_blendif_select_colorspace), module);
3022 gtk_menu_shell_append(GTK_MENU_SHELL(menu), mi);
3023
3024 mi = gtk_check_menu_item_new_with_label(_("RGB (scene)"));
3025
3026 if(module_blend_cst == DEVELOP_BLEND_CS_RGB_SCENE)
3027 {
3028 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(mi), TRUE);
3029 dt_gui_add_class(mi, "active_menu_item");
3030 }
3031 g_object_set_data_full(G_OBJECT(mi), "dt-blend-cst", GINT_TO_POINTER(DEVELOP_BLEND_CS_RGB_SCENE), NULL);
3032 g_signal_connect(G_OBJECT(mi), "activate", G_CALLBACK(_blendif_select_colorspace), module);
3033 gtk_menu_shell_append(GTK_MENU_SHELL(menu), mi);
3034
3035 gtk_menu_shell_append(GTK_MENU_SHELL(menu), gtk_separator_menu_item_new());
3036
3037 if(bd->output_channels_shown)
3038 {
3039 mi = gtk_menu_item_new_with_label(_("reset and hide output channels"));
3040 g_signal_connect(G_OBJECT(mi), "activate", G_CALLBACK(_blendif_hide_output_channels), module);
3041 gtk_menu_shell_append(GTK_MENU_SHELL(menu), mi);
3042 }
3043 else
3044 {
3045 mi = gtk_menu_item_new_with_label(_("show output channels"));
3046 g_signal_connect(G_OBJECT(mi), "activate", G_CALLBACK(_blendif_show_output_channels), module);
3047 gtk_menu_shell_append(GTK_MENU_SHELL(menu), mi);
3048 }
3049 }
3050
3051 dt_gui_menu_popup(darktable.gui->presets_popup_menu, GTK_WIDGET(button), GDK_GRAVITY_SOUTH_EAST, GDK_GRAVITY_NORTH_EAST);
3052
3054}
3055
3056// activate channel/mask view
3058{
3059 // FIXME : there are more than 3 functions getting clever about how to setup module->request_mask_display depending on user input.
3060 // These should all use an uniform setter function.
3061 //
3062 // The lack of setter implies we don't guaranty that only 1 module can request mask display at a time.
3063 // The consequence is pipeline needs to check if module->request_mask_display AND module == dev->gui_module,
3064 // but the global pipe->mask_display is set from *_blend_process() at runtime, so it's a pipe property
3065 // that changes over the pipe lifecycle.
3066 //
3067 // This is a self-feeding loop of madness because it ties the pipeline to GUI states
3068 // (but not all pipes are connected to a GUI, so you need to cover all cases all the time and don't forget to test everything),
3069 // and because the pipeline is executed recursively from the end, but pipe->mask_display is set in the middle,
3070 // when it reaches the process() method of the module capturing mask preview, so you don't have this info when
3071 // planning for pipeline execution.
3072 // And you need to plan for mask preview ahead in pipe because mask preview needs to work
3073 // without using the pixelpipe cache, at least between the module requiring mask preview and gamma.c, which will actually render
3074 // the preview at the far end of the pipe.
3075 // So the not-so-clever workaround inherited from darktable was to flush all cache lines when requesting mask preview,
3076 // which flushed lines that could be reused later and were only temporarily not needed.
3077
3078 dt_iop_gui_blend_data_t *data = module->blend_data;
3079
3080 dt_dev_pixelpipe_display_mask_t new_request_mask_display = module->request_mask_display | mode;
3081
3082 // in case user requests channel display: get the cannel
3083 if(new_request_mask_display & DT_DEV_PIXELPIPE_DISPLAY_CHANNEL)
3084 {
3086
3087 if(widget == GTK_WIDGET(data->filter[1].slider))
3089
3090 new_request_mask_display &= ~DT_DEV_PIXELPIPE_DISPLAY_ANY;
3091 new_request_mask_display |= channel;
3092 }
3093
3094 // only if something has changed: reprocess center view
3095 if(new_request_mask_display != module->request_mask_display)
3096 {
3097 module->request_mask_display = new_request_mask_display;
3101 }
3102}
3103
3104// toggle channel/mask view
3106{
3107 // FIXME : there are more than 3 functions getting clever about how to setup module->request_mask_display depending on user input.
3108 // These should all use an uniform setter function.
3109 //
3110 // The lack of setter implies we don't guaranty that only 1 module can request mask display at a time.
3111 // The consequence is pipeline needs to check if module->request_mask_display AND module == dev->gui_module,
3112 // but the global pipe->mask_display is set from *_blend_process() at runtime, so it's a pipe property
3113 // that changes over the pipe lifecycle.
3114 //
3115 // This is a self-feeding loop of madness because it ties the pipeline to GUI states
3116 // (but not all pipes are connected to a GUI, so you need to cover all cases all the time and don't forget to test everything),
3117 // and because the pipeline is executed recursively from the end, but pipe->mask_display is set in the middle,
3118 // when it reaches the process() method of the module capturing mask preview, so you don't have this info when
3119 // planning for pipeline execution.
3120 // And you need to plan for mask preview ahead in pipe because mask preview needs to work
3121 // without using the pixelpipe cache, at least between the module requiring mask preview and gamma.c, which will actually render
3122 // the preview at the far end of the pipe.
3123 // So the not-so-clever workaround inherited from darktable was to flush all cache lines when requesting mask preview,
3124 // which flushed lines that could be reused later and were only temporarily not needed.
3125
3126 dt_iop_gui_blend_data_t *data = module->blend_data;
3127
3128 dt_dev_pixelpipe_display_mask_t new_request_mask_display = module->request_mask_display & ~DT_DEV_PIXELPIPE_DISPLAY_STICKY;
3129
3130 // toggle mode
3131 if(module->request_mask_display & mode)
3132 new_request_mask_display &= ~mode;
3133 else
3134 new_request_mask_display |= mode;
3135
3137 if(new_request_mask_display & DT_DEV_PIXELPIPE_DISPLAY_STICKY)
3139 else
3140 data->save_for_leave &= ~DT_DEV_PIXELPIPE_DISPLAY_STICKY;
3142
3143 new_request_mask_display &= ~DT_DEV_PIXELPIPE_DISPLAY_ANY;
3144
3145 // in case user requests channel display: get the cannel
3146 if(new_request_mask_display & DT_DEV_PIXELPIPE_DISPLAY_CHANNEL)
3147 {
3149
3150 if(widget == GTK_WIDGET(data->filter[1].slider))
3152
3153 new_request_mask_display &= ~DT_DEV_PIXELPIPE_DISPLAY_ANY;
3154 new_request_mask_display |= channel;
3155 }
3156
3157 if(new_request_mask_display != module->request_mask_display)
3158 {
3159 module->request_mask_display = new_request_mask_display;
3163 }
3164}
3165
3175{
3176 dt_iop_gui_blend_data_t *data = !IS_NULL_PTR(module) ? module->blend_data : NULL;
3177 if(IS_NULL_PTR(data)) return;
3178
3179 const gboolean channel_active
3180 = (module->request_mask_display & DT_DEV_PIXELPIPE_DISPLAY_CHANNEL) != 0;
3181 const gboolean output_active
3182 = channel_active && (module->request_mask_display & DT_DEV_PIXELPIPE_DISPLAY_OUTPUT) != 0;
3183
3184 ++darktable.gui->reset;
3185 if(GTK_IS_TOGGLE_BUTTON(data->filter[0].channel_display))
3186 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(data->filter[0].channel_display),
3187 channel_active && !output_active);
3188 if(GTK_IS_TOGGLE_BUTTON(data->filter[1].channel_display))
3189 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(data->filter[1].channel_display),
3190 output_active);
3191 --darktable.gui->reset;
3192}
3193
3202static void _blendop_blendif_channel_display_toggled(GtkToggleButton *button, dt_iop_module_t *module)
3203{
3204 if(darktable.gui->reset) return;
3205
3206 dt_iop_gui_blend_data_t *data = !IS_NULL_PTR(module) ? module->blend_data : NULL;
3207 if(IS_NULL_PTR(data) || IS_NULL_PTR(data->channel)) return;
3208
3209 const int in_out = GTK_WIDGET(button) == data->filter[1].channel_display ? 1 : 0;
3210 const gboolean active = gtk_toggle_button_get_active(button);
3211 GtkWidget *const other_button = data->filter[!in_out].channel_display;
3212
3213 ++darktable.gui->reset;
3214 if(active && GTK_IS_TOGGLE_BUTTON(other_button))
3215 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(other_button), FALSE);
3216 --darktable.gui->reset;
3217
3218 dt_dev_pixelpipe_display_mask_t new_request_mask_display
3219 = module->request_mask_display
3220 & ~(DT_DEV_PIXELPIPE_DISPLAY_CHANNEL | DT_DEV_PIXELPIPE_DISPLAY_OUTPUT
3221 | DT_DEV_PIXELPIPE_DISPLAY_ANY | DT_DEV_PIXELPIPE_DISPLAY_STICKY);
3222
3223 if(active)
3224 {
3225 new_request_mask_display |= DT_DEV_PIXELPIPE_DISPLAY_CHANNEL
3227 | data->channel[data->tab].display_channel;
3228 if(in_out) new_request_mask_display |= DT_DEV_PIXELPIPE_DISPLAY_OUTPUT;
3229 }
3230
3232 if(active)
3234 else
3235 data->save_for_leave &= ~DT_DEV_PIXELPIPE_DISPLAY_STICKY;
3237
3238 if(new_request_mask_display == module->request_mask_display) return;
3239
3240 module->request_mask_display = new_request_mask_display;
3242
3243 dt_iop_request_focus(module);
3245}
3246
3247
3248// magic mode: if mouse cursor enters a gradient slider with shift and/or control pressed we
3249// enter channel display and/or mask display mode
3250static gboolean _blendop_blendif_enter(GtkWidget *widget, GdkEventCrossing *event, dt_iop_module_t *module)
3251{
3252 // FIXME : there are more than 3 functions getting clever about how to setup module->request_mask_display depending on user input.
3253 // These should all use an uniform setter function.
3254 //
3255 // The lack of setter implies we don't guaranty that only 1 module can request mask display at a time.
3256 // The consequence is pipeline needs to check if module->request_mask_display AND module == dev->gui_module,
3257 // but the global pipe->mask_display is set from *_blend_process() at runtime, so it's a pipe property
3258 // that changes over the pipe lifecycle.
3259 //
3260 // This is a self-feeding loop of madness because it ties the pipeline to GUI states
3261 // (but not all pipes are connected to a GUI, so you need to cover all cases all the time and don't forget to test everything),
3262 // and because the pipeline is executed recursively from the end, but pipe->mask_display is set in the middle,
3263 // when it reaches the process() method of the module capturing mask preview, so you don't have this info when
3264 // planning for pipeline execution.
3265 // And you need to plan for mask preview ahead in pipe because mask preview needs to work
3266 // without using the pixelpipe cache, at least between the module requiring mask preview and gamma.c, which will actually render
3267 // the preview at the far end of the pipe.
3268 // So the not-so-clever workaround inherited from darktable was to flush all cache lines when requesting mask preview,
3269 // which flushed lines that could be reused later and were only temporarily not needed.
3270
3271 if(darktable.gui->reset) return FALSE;
3272 dt_iop_gui_blend_data_t *data = module->blend_data;
3273
3275
3276 // depending on shift modifiers we activate channel and/or mask display
3277 if(dt_modifier_is(event->state, GDK_SHIFT_MASK | GDK_CONTROL_MASK))
3278 {
3280 }
3281 else if(dt_modifier_is(event->state, GDK_SHIFT_MASK))
3282 {
3284 }
3285 else if(dt_modifier_is(event->state, GDK_CONTROL_MASK))
3286 {
3288 }
3289
3291 if(mode && data->timeout_handle)
3292 {
3293 // purge any remaining timeout handlers
3294 g_source_remove(data->timeout_handle);
3295 data->timeout_handle = 0;
3296 }
3298 {
3299 // save request_mask_display to restore later
3300 data->save_for_leave = module->request_mask_display & ~DT_DEV_PIXELPIPE_DISPLAY_STICKY;
3301 }
3303
3304 /* A plain hover only gives keyboard focus to the slider. Reapplying an
3305 * already-active CHANNEL request here would derive INPUT/OUTPUT again from
3306 * the hovered widget and switch the persistent channel toggle without a
3307 * click. Only modifier-assisted hover is allowed to change the preview. */
3309 _blendop_blendif_channel_mask_view(widget, module, mode);
3310
3311 gtk_widget_grab_focus(widget);
3312 return FALSE;
3313}
3314
3315
3316// handler for delayed mask/channel display mode switch-off
3317static gboolean _blendop_blendif_leave_delayed(gpointer data)
3318{
3319 // FIXME : there are more than 3 functions getting clever about how to setup module->request_mask_display depending on user input.
3320 // These should all use an uniform setter function.
3321 //
3322 // The lack of setter implies we don't guaranty that only 1 module can request mask display at a time.
3323 // The consequence is pipeline needs to check if module->request_mask_display AND module == dev->gui_module,
3324 // but the global pipe->mask_display is set from *_blend_process() at runtime, so it's a pipe property
3325 // that changes over the pipe lifecycle.
3326 //
3327 // This is a self-feeding loop of madness because it ties the pipeline to GUI states
3328 // (but not all pipes are connected to a GUI, so you need to cover all cases all the time and don't forget to test everything),
3329 // and because the pipeline is executed recursively from the end, but pipe->mask_display is set in the middle,
3330 // when it reaches the process() method of the module capturing mask preview, so you don't have this info when
3331 // planning for pipeline execution.
3332 // And you need to plan for mask preview ahead in pipe because mask preview needs to work
3333 // without using the pixelpipe cache, at least between the module requiring mask preview and gamma.c, which will actually render
3334 // the preview at the far end of the pipe.
3335 // So the not-so-clever workaround inherited from darktable was to flush all cache lines when requesting mask preview,
3336 // which flushed lines that could be reused later and were only temporarily not needed.
3337
3338 dt_iop_module_t *module = (dt_iop_module_t *)data;
3339 dt_iop_gui_blend_data_t *bd = module->blend_data;
3340 int reprocess = 0;
3341
3342 dt_pthread_mutex_lock(&bd->lock);
3343 // restore saved request_mask_display and reprocess image
3344 if(bd->timeout_handle && (module->request_mask_display != (bd->save_for_leave & ~DT_DEV_PIXELPIPE_DISPLAY_STICKY)))
3345 {
3346 module->request_mask_display = bd->save_for_leave & ~DT_DEV_PIXELPIPE_DISPLAY_STICKY;
3348 reprocess = 1;
3349 }
3350 bd->timeout_handle = 0;
3351 dt_pthread_mutex_unlock(&bd->lock);
3352
3353 if(reprocess)
3354 {
3357 }
3358 // return FALSE and thereby terminate the handler
3359 return FALSE;
3360}
3361
3362// de-activate magic mode when leaving the gradient slider
3363static gboolean _blendop_blendif_leave(GtkWidget *widget, GdkEventCrossing *event, dt_iop_module_t *module)
3364{
3365 if(darktable.gui->reset) return FALSE;
3366 dt_iop_gui_blend_data_t *data = module->blend_data;
3367
3368 // do not immediately switch-off mask/channel display in case user leaves gradient only briefly.
3369 // instead we activate a handler function that gets triggered after some timeout
3373 data->timeout_handle = g_timeout_add(1000, _blendop_blendif_leave_delayed, module);
3375
3376 return FALSE;
3377}
3378
3379
3380static gboolean _blendop_blendif_key_press(GtkWidget *widget, GdkEventKey *event, dt_iop_module_t *module)
3381{
3382 if(darktable.gui->reset) return FALSE;
3383
3384 gboolean handled = FALSE;
3385
3386 switch(event->keyval)
3387 {
3388 case GDK_KEY_m:
3389 case GDK_KEY_M:
3391 handled = TRUE;
3392 }
3393
3394 if(handled)
3395 dt_iop_request_focus(module);
3396
3397 return handled;
3398}
3399
3400
3401#define COLORSTOPS(gradient) sizeof(gradient) / sizeof(dt_iop_gui_blendif_colorstop_t), gradient
3402
3404 = { { N_("L"), N_("sliders for L channel"), 1.0f / 100.0f, COLORSTOPS(_gradient_L), TRUE, 0.0f,
3407 { N_("a"), N_("sliders for a channel"), 1.0f / 256.0f, COLORSTOPS(_gradient_a), TRUE, 0.0f,
3410 { N_("b"), N_("sliders for b channel"), 1.0f / 256.0f, COLORSTOPS(_gradient_b), TRUE, 0.0f,
3413 { N_("C"), N_("sliders for chroma channel (of LCh)"), 1.0f / 100.0f, COLORSTOPS(_gradient_chroma),
3414 TRUE, 0.0f,
3417 { N_("h"), N_("sliders for hue channel (of LCh)"), 1.0f / 360.0f, COLORSTOPS(_gradient_LCh_hue),
3418 FALSE, 0.0f,
3420 _blendif_scale_print_hue, NULL, N_("hue") },
3421 { NULL } };
3422
3424 = { { N_("g"), N_("sliders for gray value"), 1.0f / 255.0f, COLORSTOPS(_gradient_gray), TRUE, 0.0f,
3427 { N_("R"), N_("sliders for red channel"), 1.0f / 255.0f, COLORSTOPS(_gradient_red), TRUE, 0.0f,
3430 { N_("G"), N_("sliders for green channel"), 1.0f / 255.0f, COLORSTOPS(_gradient_green), TRUE, 0.0f,
3433 { N_("B"), N_("sliders for blue channel"), 1.0f / 255.0f, COLORSTOPS(_gradient_blue), TRUE, 0.0f,
3436 { N_("H"), N_("sliders for hue channel (of HSL)"), 1.0f / 360.0f, COLORSTOPS(_gradient_HSL_hue),
3437 FALSE, 0.0f,
3439 _blendif_scale_print_hue, NULL, N_("hue") },
3440 { N_("S"), N_("sliders for chroma channel (of HSL)"), 1.0f / 100.0f, COLORSTOPS(_gradient_chroma),
3441 FALSE, 0.0f,
3444 { N_("L"), N_("sliders for value channel (of HSL)"), 1.0f / 100.0f, COLORSTOPS(_gradient_gray),
3445 FALSE, 0.0f,
3448 { NULL } };
3449
3451 = { { N_("g"), N_("sliders for gray value"), 1.0f / 255.0f, COLORSTOPS(_gradient_gray), TRUE, 0.0f,
3454 { N_("R"), N_("sliders for red channel"), 1.0f / 255.0f, COLORSTOPS(_gradient_red), TRUE, 0.0f,
3457 { N_("G"), N_("sliders for green channel"), 1.0f / 255.0f, COLORSTOPS(_gradient_green), TRUE, 0.0f,
3460 { N_("B"), N_("sliders for blue channel"), 1.0f / 255.0f, COLORSTOPS(_gradient_blue), TRUE, 0.0f,
3463 { N_("Jz"), N_("sliders for value channel (of JzCzhz)"), 1.0f / 100.0f, COLORSTOPS(_gradient_gray),
3464 TRUE, -6.64385619f, // cf. _blend_init_blendif_boost_parameters
3467 { N_("Cz"), N_("sliders for chroma channel (of JzCzhz)"), 1.0f / 100.0f, COLORSTOPS(_gradient_chroma),
3468 TRUE, -6.64385619f, // cf. _blend_init_blendif_boost_parameters
3471 { N_("hz"), N_("sliders for hue channel (of JzCzhz)"), 1.0f / 360.0f, COLORSTOPS(_gradient_JzCzhz_hue),
3472 FALSE, 0.0f,
3474 _blendif_scale_print_hue, NULL, N_("hue") },
3475 { NULL } };
3476
3477const char *slider_tooltip[] = { N_("adjustment based on input received by this module:\n* range defined by upper markers: "
3478 "blend fully\n* range defined by lower markers: do not blend at all\n* range between "
3479 "adjacent upper/lower markers: blend gradually"),
3480 N_("adjustment based on unblended output of this module:\n* range defined by upper "
3481 "markers: blend fully\n* range defined by lower markers: do not blend at all\n* range "
3482 "between adjacent upper/lower markers: blend gradually") };
3483
3485{
3486 // FIXME : there are more than 3 functions getting clever about how to setup module->request_mask_display depending on user input.
3487 // These should all use an uniform setter function.
3488 //
3489 // The lack of setter implies we don't guaranty that only 1 module can request mask display at a time.
3490 // The consequence is pipeline needs to check if module->request_mask_display AND module == dev->gui_module,
3491 // but the global pipe->mask_display is set from *_blend_process() at runtime, so it's a pipe property
3492 // that changes over the pipe lifecycle.
3493 //
3494 // This is a self-feeding loop of madness because it ties the pipeline to GUI states
3495 // (but not all pipes are connected to a GUI, so you need to cover all cases all the time and don't forget to test everything),
3496 // and because the pipeline is executed recursively from the end, but pipe->mask_display is set in the middle,
3497 // when it reaches the process() method of the module capturing mask preview, so you don't have this info when
3498 // planning for pipeline execution.
3499 // And you need to plan for mask preview ahead in pipe because mask preview needs to work
3500 // without using the pixelpipe cache, at least between the module requiring mask preview and gamma.c, which will actually render
3501 // the preview at the far end of the pipe.
3502 // So the not-so-clever workaround inherited from darktable was to flush all cache lines when requesting mask preview,
3503 // which flushed lines that could be reused later and were only temporarily not needed.
3504 dt_iop_gui_blend_data_t *bd = module->blend_data;
3505
3506 if(IS_NULL_PTR(bd) || !bd->blendif_support || !bd->blendif_inited) return;
3507
3508 ++darktable.gui->reset;
3509
3511 if(bd->timeout_handle)
3512 {
3513 g_source_remove(bd->timeout_handle);
3514 bd->timeout_handle = 0;
3516 {
3517 module->request_mask_display = bd->save_for_leave & ~DT_DEV_PIXELPIPE_DISPLAY_STICKY;
3520 }
3521 }
3523
3524 /* update output channel mask visibility */
3525 gtk_widget_set_visible(GTK_WIDGET(bd->filter[1].box), bd->output_channels_shown);
3526
3527 /* update tabs */
3528 if(bd->channel_tabs_csp != bd->csp)
3529 {
3530 bd->channel = NULL;
3531
3532 switch(bd->csp)
3533 {
3535 bd->channel = Lab_channels;
3536 break;
3538 bd->channel = rgb_channels;
3539 break;
3541 bd->channel = rgbj_channels;
3542 break;
3543 default:
3544 assert(FALSE); // blendif not supported for RAW, which is already caught upstream; we should not get
3545 // here
3546 }
3547
3549
3550 /* remove tabs before adding others */
3552
3553 bd->channel_tabs_csp = bd->csp;
3554
3555 int index = 0;
3556 for(const dt_iop_gui_blendif_channel_t *ch = bd->channel; !IS_NULL_PTR(ch->label); ch++, index++)
3557 {
3558 dt_ui_notebook_page(bd->channel_tabs, ch->label, _(ch->tooltip));
3559 gtk_widget_show_all(GTK_WIDGET(gtk_notebook_get_nth_page(bd->channel_tabs, index)));
3560 }
3561
3562 bd->tab = 0;
3563 gtk_notebook_set_current_page(GTK_NOTEBOOK(bd->channel_tabs), bd->tab);
3564 }
3565
3566 _blendop_blendif_update_tab(module, bd->tab);
3568
3569 --darktable.gui->reset;
3570}
3571
3572
3573void dt_iop_gui_init_blendif(GtkBox *blendw, dt_iop_module_t *module, GtkWidget *blendif_header)
3574{
3576
3577 bd->blendif_box = blendw;
3578
3579 /* create and add blendif support if module supports it */
3580 if(bd->blendif_support)
3581 {
3582 dt_iop_togglebutton_new_no_register(module, "blend`tools", N_("reset blend mask settings"), NULL,
3583 G_CALLBACK(_blendop_blendif_reset), FALSE, 0, 0,
3584 dtgtk_cairo_paint_reset, blendif_header);
3585
3586 GtkWidget *header = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, DT_GUI_BOX_SPACING);
3587 gtk_widget_set_name(header, "blendif-pickers");
3588
3589 bd->tab = 0;
3591 bd->channel_tabs = GTK_NOTEBOOK(gtk_notebook_new());
3592 gtk_notebook_set_scrollable(bd->channel_tabs, TRUE);
3593 gtk_box_pack_start(GTK_BOX(bd->blendif_box), GTK_WIDGET(bd->channel_tabs), FALSE, FALSE, 0);
3594 gtk_notebook_set_action_widget(GTK_NOTEBOOK(bd->channel_tabs), header, GTK_PACK_END);
3595 dt_gui_add_class(GTK_WIDGET(bd->channel_tabs), "empty");
3596
3598 gtk_widget_set_tooltip_text(bd->colorpicker, _("pick GUI color from image\nctrl+click or right-click to select an area"));
3599
3603
3604 gtk_widget_set_tooltip_text(bd->colorpicker_set_values, _("set the range based on an area from the image\n"
3605 "drag to use the input image\n"
3606 "ctrl+drag to use the output image"));
3607
3608 dt_iop_togglebutton_new_no_register(module, "blend`tools", N_("invert all channel's polarities"), NULL,
3609 G_CALLBACK(_blendop_blendif_invert), FALSE, 0, 0,
3610 dtgtk_cairo_paint_invert, header);
3611
3612 gtk_widget_show_all(header);
3613
3614 for(int in_out = 1; in_out >= 0; in_out--)
3615 {
3616 dt_iop_gui_blendif_filter_t *sl = &bd->filter[in_out];
3617
3618 GtkWidget *slider_box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, DT_GUI_BOX_SPACING);
3619
3621 in_out ? "blend-upper" : "blend-lower"));
3622 gtk_box_pack_start(GTK_BOX(slider_box), GTK_WIDGET(sl->slider), TRUE, TRUE, 0);
3623
3625 dt_gui_add_class(sl->polarity, "dt_ignore_fg_state");
3626
3627 gtk_widget_set_tooltip_text(sl->polarity, _("toggle polarity. best seen by enabling 'display mask'"));
3628 gtk_box_pack_end(GTK_BOX(slider_box), GTK_WIDGET(sl->polarity), FALSE, FALSE, 0);
3629
3630 GtkWidget *label_box = gtk_grid_new();
3631 gtk_grid_set_column_homogeneous(GTK_GRID(label_box), TRUE);
3632
3633 sl->head = GTK_LABEL(dt_ui_label_new(in_out ? _("output") : _("input")));
3634 gtk_grid_attach(GTK_GRID(label_box), GTK_WIDGET(sl->head), 0, 0, 1, 1);
3635
3636 GtkWidget *overlay = gtk_overlay_new();
3637 gtk_grid_attach(GTK_GRID(label_box), overlay, 1, 0, 3, 1);
3638
3639 sl->picker_label = GTK_LABEL(gtk_label_new(""));
3640 gtk_widget_set_name(GTK_WIDGET(sl->picker_label), "blend-data");
3641 gtk_label_set_xalign(sl->picker_label, .0);
3642 gtk_label_set_yalign(sl->picker_label, 1.0);
3643 gtk_container_add(GTK_CONTAINER(overlay), GTK_WIDGET(sl->picker_label));
3644
3645 for(int k = 0; k < 4; k++)
3646 {
3647 sl->label[k] = GTK_LABEL(gtk_label_new(NULL));
3648 gtk_widget_set_name(GTK_WIDGET(sl->label[k]), "blend-data");
3649 gtk_label_set_xalign(sl->label[k], .35 + k * .65/3);
3650 gtk_label_set_yalign(sl->label[k], k % 2);
3651 gtk_overlay_add_overlay(GTK_OVERLAY(overlay), GTK_WIDGET(sl->label[k]));
3652 }
3653
3654 gtk_widget_set_tooltip_text(GTK_WIDGET(sl->slider),
3655 _("double-click to reset.\npress 'm' to toggle mask view."));
3656 gtk_widget_set_tooltip_text(GTK_WIDGET(sl->head), _(slider_tooltip[in_out]));
3657
3658 g_signal_connect(G_OBJECT(sl->slider), "value-changed", G_CALLBACK(_blendop_blendif_sliders_callback), bd);
3659 g_signal_connect(G_OBJECT(sl->slider), "value-reset", G_CALLBACK(_blendop_blendif_sliders_reset_callback), bd);
3660 g_signal_connect(G_OBJECT(sl->slider), "leave-notify-event", G_CALLBACK(_blendop_blendif_leave), module);
3661 g_signal_connect(G_OBJECT(sl->slider), "enter-notify-event", G_CALLBACK(_blendop_blendif_enter), module);
3662 g_signal_connect(G_OBJECT(sl->slider), "key-press-event", G_CALLBACK(_blendop_blendif_key_press), module);
3663 g_signal_connect(G_OBJECT(sl->polarity), "toggled", G_CALLBACK(_blendop_blendif_polarity_callback), bd);
3664
3665 // The label is semantically "part of" the slider, even though it's a different widget,
3666 // so the parent box has no internal spacing between children to keep them grouped.
3667 sl->box = GTK_BOX(gtk_box_new(GTK_ORIENTATION_VERTICAL, 0));
3668 gtk_box_pack_start(GTK_BOX(sl->box), GTK_WIDGET(label_box), FALSE, FALSE, 0);
3669 gtk_box_pack_start(GTK_BOX(sl->box), GTK_WIDGET(slider_box), FALSE, FALSE, 0);
3670
3671 GtkWidget *display_controls = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, DT_GUI_BOX_SPACING);
3672
3673 sl->log_scale = gtk_toggle_button_new_with_label(_("Log scale"));
3674 gtk_widget_set_tooltip_text(
3675 sl->log_scale,
3676 _("toggle the alternative logarithmic or magnified scale for this channel"));
3677 g_signal_connect(G_OBJECT(sl->log_scale), "toggled",
3678 G_CALLBACK(_blendop_blendif_log_scale_toggled), module);
3679 gtk_box_pack_start(GTK_BOX(display_controls), sl->log_scale, FALSE, FALSE, 0);
3680
3682 gtk_widget_set_tooltip_text(
3683 sl->channel_display,
3684 in_out ? _("display the current channel from the unblended module output")
3685 : _("display the current channel from the module input"));
3686 g_signal_connect(G_OBJECT(sl->channel_display), "toggled",
3687 G_CALLBACK(_blendop_blendif_channel_display_toggled), module);
3688 gtk_box_pack_start(GTK_BOX(display_controls), sl->channel_display, FALSE, FALSE, 0);
3689
3690 gtk_box_pack_start(GTK_BOX(sl->box), display_controls, FALSE, FALSE, 0);
3691
3692 gtk_box_pack_start(GTK_BOX(bd->blendif_box), GTK_WIDGET(sl->box), FALSE, FALSE, 0);
3693 }
3694
3701 gtk_widget_set_tooltip_text(bd->channel_boost_factor_slider, _("adjust the boost factor of the channel mask"));
3702 gtk_widget_set_sensitive(bd->channel_boost_factor_slider, FALSE);
3703
3704 g_signal_connect(G_OBJECT(bd->channel_boost_factor_slider), "value-changed",
3706
3707 gtk_box_pack_start(GTK_BOX(bd->blendif_box), GTK_WIDGET(bd->channel_boost_factor_slider), FALSE, FALSE, 0);
3708
3709 g_signal_connect(G_OBJECT(bd->channel_tabs), "switch_page", G_CALLBACK(_blendop_blendif_tab_switch), bd);
3710 g_signal_connect(G_OBJECT(bd->colorpicker), "toggled", G_CALLBACK(_update_gradient_slider_pickers), module);
3711 g_signal_connect(G_OBJECT(bd->colorpicker_set_values), "toggled", G_CALLBACK(_update_gradient_slider_pickers), module);
3712
3713 bd->blendif_inited = 1;
3714 }
3715 else
3716 {
3717 gchar *module_name = dt_history_item_get_name(module);
3718 gchar *markup = g_markup_printf_escaped(
3719 _("<i>Parametric masking is disabled because the <b>%s</b> module does not provide compatible color channels.</i>"),
3720 module_name);
3721 GtkWidget *label = gtk_label_new(NULL);
3722 gtk_label_set_markup(GTK_LABEL(label), markup);
3723 gtk_label_set_xalign(GTK_LABEL(label), 0.0f);
3724 gtk_label_set_line_wrap(GTK_LABEL(label), TRUE);
3725 gtk_box_pack_start(GTK_BOX(bd->blendif_box), label, FALSE, FALSE, 0);
3726 dt_free(markup);
3727 dt_free(module_name);
3728 }
3729}
3730
3731void dt_iop_gui_init_contours(GtkBox *blendw, dt_iop_module_t *module)
3732{
3734
3735 gtk_box_pack_start(blendw, bd->details_slider, FALSE, FALSE, 0);
3736 gtk_box_pack_start(blendw, bd->blur_radius_slider, FALSE, FALSE, 0);
3737 gtk_box_pack_start(blendw, bd->masks_feathering_guide_combo, FALSE, FALSE, 0);
3738 gtk_box_pack_start(blendw, bd->feathering_radius_slider, FALSE, FALSE, 0);
3739 gtk_box_pack_start(blendw, bd->brightness_slider, FALSE, FALSE, 0);
3740 gtk_box_pack_start(blendw, bd->contrast_slider, FALSE, FALSE, 0);
3741}
3742
3744{
3746 dt_develop_blend_params_t *bp = module->blend_params;
3747
3748 if(IS_NULL_PTR(bd) || !bd->masks_support || !bd->masks_inited) return;
3749
3750 ++darktable.gui->reset;
3751
3752 /* update masks state */
3753 dt_masks_form_gui_t *form_gui = module->dev->form_gui;
3754 dt_masks_form_t *visible_form = dt_masks_get_visible_form(module->dev);
3755 const gboolean module_creation = !IS_NULL_PTR(form_gui) && form_gui->creation
3756 && form_gui->creation_module == module;
3757 const dt_masks_type_t creation_type = module_creation && !IS_NULL_PTR(visible_form)
3758 ? visible_form->type
3759 : (module_creation ? form_gui->creation_type : DT_MASKS_NONE);
3761 const gboolean has_group_shapes = grp && (grp->type & DT_MASKS_GROUP) && grp->points;
3762 if(GTK_IS_WIDGET(bd->masks_combo))
3763 {
3765 if(has_group_shapes)
3766 {
3767 char txt[512];
3768 const guint n = g_list_length(grp->points);
3769 snprintf(txt, sizeof(txt), ngettext("%d shape used", "%d shapes used", n), n);
3771 }
3772 else
3773 {
3774 dt_bauhaus_combobox_add(bd->masks_combo, _("no mask used"));
3775 }
3777 gtk_widget_queue_draw(bd->masks_combo);
3778 }
3779
3780 if(!has_group_shapes)
3781 {
3783 // reset the gui
3785 }
3786
3787 if(bd->masks_support)
3788 {
3789 if(GTK_IS_TOGGLE_BUTTON(bd->masks_edit))
3790 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(bd->masks_edit),
3791 !module_creation && bd->masks_shown != DT_MASKS_EDIT_OFF);
3792
3793 if(GTK_IS_TOGGLE_BUTTON(bd->masks_polarity))
3794 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(bd->masks_polarity),
3796 }
3797
3798 // update buttons status
3799 for(int n = 0; n < DEVELOP_MASKS_NB_SHAPES; n++)
3800 {
3801 if(!GTK_IS_TOGGLE_BUTTON(bd->masks_shapes[n])) continue;
3802
3803 if(module_creation && (creation_type & bd->masks_type[n]))
3804 {
3805 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(bd->masks_shapes[n]), TRUE);
3806 }
3807 else
3808 {
3809 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(bd->masks_shapes[n]), FALSE);
3810 }
3811 }
3812
3815
3816 --darktable.gui->reset;
3817}
3818
3819void dt_iop_gui_init_masks(GtkBox *blendw, dt_iop_module_t *module)
3820{
3822
3823 bd->masks_box = blendw;
3824
3825 /* create and add masks support if module supports it */
3826 if(bd->masks_support)
3827 {
3828 bd->masks_combo_ids = NULL;
3830
3831 bd->lists_box = gtk_box_new(GTK_ORIENTATION_VERTICAL, DT_GUI_BOX_SPACING);
3832
3833 GtkCellRenderer *renderer = NULL;
3834 GtkTreeSelection *selection = NULL;
3835
3836 GtkWidget *group_shapes_header = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, DT_GUI_BOX_SPACING);
3837 bd->group_shapes_label = gtk_entry_new();
3838 gchar *group_placeholder = dt_dev_get_masks_group_name(module);
3839 gtk_entry_set_placeholder_text(GTK_ENTRY(bd->group_shapes_label), group_placeholder);
3840 g_free(group_placeholder);
3841 gtk_widget_set_tooltip_text(bd->group_shapes_label, _("Edit current module mask name"));
3842 gtk_widget_set_halign(bd->group_shapes_label, GTK_ALIGN_FILL);
3843 gtk_widget_set_hexpand(bd->group_shapes_label, TRUE);
3844 g_signal_connect(bd->group_shapes_label, "activate", G_CALLBACK(_blendop_masks_group_name_activate), module);
3845 g_signal_connect(bd->group_shapes_label, "focus-out-event",
3846 G_CALLBACK(_blendop_masks_group_name_focus_out), module);
3847 gtk_box_pack_start(GTK_BOX(group_shapes_header), bd->group_shapes_label, TRUE, TRUE, 0);
3848
3849 // buttons for mask polarity and edit mode
3850 bd->masks_polarity = dt_iop_togglebutton_new_no_register(module, "blend`tools", N_("toggle polarity of drawn mask"), NULL,
3852 FALSE, 0, 0, dtgtk_cairo_paint_invert, group_shapes_header);
3854 dt_gui_add_class(bd->masks_polarity, "dt_ignore_fg_state");
3855
3856 bd->masks_edit = dt_iop_togglebutton_new_no_register(module, "blend`tools", N_("show and edit mask elements"),
3857 N_("show and edit in restricted mode"),
3858 G_CALLBACK(_blendop_masks_show_and_edit),
3859 FALSE, 0, 0, dtgtk_cairo_paint_masks_edit, group_shapes_header);
3860
3861 gtk_box_pack_start(GTK_BOX(bd->lists_box), group_shapes_header, FALSE, FALSE, 0);
3862
3863 // Current mask list
3864 bd->masks_group_treeview = gtk_tree_view_new();
3865 bd->group_shapes_store = gtk_tree_store_new(BLENDOP_MASKS_GROUP_COL_COUNT, GDK_TYPE_PIXBUF,
3866 GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_INT,
3867 G_TYPE_INT, G_TYPE_INT, G_TYPE_INT);
3868 gtk_tree_view_set_model(GTK_TREE_VIEW(bd->masks_group_treeview), GTK_TREE_MODEL(bd->group_shapes_store));
3869
3870 // Initialize mask operation icons for treeview display
3872
3873 bd->group_shapes_col = gtk_tree_view_column_new();
3874 gtk_tree_view_append_column(GTK_TREE_VIEW(bd->masks_group_treeview), bd->group_shapes_col);
3875
3876 renderer = gtk_cell_renderer_pixbuf_new();
3877 gtk_tree_view_column_pack_start(bd->group_shapes_col, renderer, FALSE);
3878 gtk_tree_view_column_add_attribute(bd->group_shapes_col, renderer, "pixbuf", BLENDOP_MASKS_GROUP_COL_OP_ICON);
3879
3880 renderer = gtk_cell_renderer_pixbuf_new();
3881 gtk_tree_view_column_pack_start(bd->group_shapes_col, renderer, FALSE);
3882 gtk_tree_view_column_add_attribute(bd->group_shapes_col, renderer, "pixbuf", BLENDOP_MASKS_GROUP_COL_INV_ICON);
3883
3884 renderer = gtk_cell_renderer_text_new();
3885 gtk_tree_view_column_pack_start(bd->group_shapes_col, renderer, TRUE);
3886 gtk_tree_view_column_add_attribute(bd->group_shapes_col, renderer, "text", BLENDOP_MASKS_GROUP_COL_NAME);
3887
3888 // Per-row action icons: unlink (detach from this mask) and delete (remove altogether).
3889 // Clicks are handled in _blendop_masks_group_button_pressed by matching the column.
3890 bd->group_unlink_col = gtk_tree_view_column_new();
3891 renderer = gtk_cell_renderer_pixbuf_new();
3892 g_object_set(renderer, "icon-name", "list-remove-symbolic", "stock-size", GTK_ICON_SIZE_MENU, NULL);
3893 gtk_tree_view_column_pack_start(bd->group_unlink_col, renderer, FALSE);
3894 gtk_tree_view_column_set_sizing(bd->group_unlink_col, GTK_TREE_VIEW_COLUMN_FIXED);
3895 gtk_tree_view_column_set_fixed_width(bd->group_unlink_col, DT_PIXEL_APPLY_DPI(24));
3896 gtk_tree_view_append_column(GTK_TREE_VIEW(bd->masks_group_treeview), bd->group_unlink_col);
3897
3898 bd->group_delete_col = gtk_tree_view_column_new();
3899 renderer = gtk_cell_renderer_pixbuf_new();
3900 g_object_set(renderer, "icon-name", "user-trash-symbolic", "stock-size", GTK_ICON_SIZE_MENU, NULL);
3901 gtk_tree_view_column_pack_start(bd->group_delete_col, renderer, FALSE);
3902 gtk_tree_view_column_set_sizing(bd->group_delete_col, GTK_TREE_VIEW_COLUMN_FIXED);
3903 gtk_tree_view_column_set_fixed_width(bd->group_delete_col, DT_PIXEL_APPLY_DPI(24));
3904 gtk_tree_view_append_column(GTK_TREE_VIEW(bd->masks_group_treeview), bd->group_delete_col);
3905
3906 // Keep the name column expanding so the action icons stay flush right.
3907 gtk_tree_view_column_set_expand(bd->group_shapes_col, TRUE);
3908
3909 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(bd->masks_group_treeview));
3910 gtk_tree_selection_set_mode(selection, GTK_SELECTION_SINGLE);
3911 g_signal_connect(selection, "changed", G_CALLBACK(_blendop_masks_group_selection_changed), module);
3912 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(bd->masks_group_treeview), FALSE);
3913 gtk_tree_view_set_show_expanders(GTK_TREE_VIEW(bd->masks_group_treeview), TRUE);
3914 g_signal_connect(bd->masks_group_treeview, "button-press-event",
3915 G_CALLBACK(_blendop_masks_group_button_pressed), module);
3916
3917 gtk_widget_set_has_tooltip(bd->masks_group_treeview, TRUE);
3918 g_signal_connect(bd->masks_group_treeview, "query-tooltip",
3919 G_CALLBACK(_blendop_masks_group_query_tooltip), module);
3920
3922 "plugins/darkroom/masks/group_list_height", DT_UI_RESIZE_DYNAMIC);
3923 gtk_widget_set_vexpand(bd->group_shapes_sw, TRUE);
3924
3925 // Creating shapes buttons (circle, ellipse ....)
3927 if(!GTK_IS_WIDGET(bd->all_shapes_buttons)) return;
3928
3929 // Wire shapes toggle button
3930 bd->wire_shape_toggle = gtk_toggle_button_new_with_label(_("Attach shapes"));
3931 gtk_widget_set_tooltip_text(bd->wire_shape_toggle, _("Show all shapes and groups to choose which ones to connect to or disconnect from the mask."));
3932
3933 GtkWidget *bottom_bar = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, DT_GUI_BOX_SPACING);
3934 gtk_box_pack_start(GTK_BOX(bottom_bar), bd->wire_shape_toggle, FALSE, FALSE, 0);
3935 gtk_box_pack_end(GTK_BOX(bottom_bar), bd->all_shapes_buttons, FALSE, FALSE, 0);
3936 gtk_box_pack_end(GTK_BOX(bd->lists_box), bottom_bar, FALSE, FALSE, 0);
3937
3938
3939 // All shapes list
3940 bd->masks_treeview = gtk_tree_view_new();
3941 bd->all_shapes_store = gtk_list_store_new(BLENDOP_MASKS_ALL_COL_COUNT, G_TYPE_BOOLEAN,
3942 G_TYPE_STRING, G_TYPE_INT, G_TYPE_BOOLEAN,
3943 G_TYPE_STRING, G_TYPE_STRING);
3944 gtk_tree_view_set_model(GTK_TREE_VIEW(bd->masks_treeview), GTK_TREE_MODEL(bd->all_shapes_store));
3945
3946 bd->all_shapes_col = gtk_tree_view_column_new();
3947 gtk_tree_view_append_column(GTK_TREE_VIEW(bd->masks_treeview), bd->all_shapes_col);
3948
3949 renderer = gtk_cell_renderer_toggle_new();
3950 g_object_set(renderer, "activatable", TRUE, NULL);
3951 g_signal_connect(renderer, "toggled", G_CALLBACK(_blendop_masks_all_toggled), module);
3952 gtk_tree_view_column_pack_start(bd->all_shapes_col, renderer, FALSE);
3953 gtk_tree_view_column_add_attribute(bd->all_shapes_col, renderer, "active", BLENDOP_MASKS_ALL_COL_ACTIVE);
3954 gtk_tree_view_column_add_attribute(bd->all_shapes_col, renderer, "sensitive", BLENDOP_MASKS_ALL_COL_SENSITIVE);
3955
3956 GtkTreeViewColumn *all_shapes_name_col = gtk_tree_view_column_new();
3957 gtk_tree_view_append_column(GTK_TREE_VIEW(bd->masks_treeview), all_shapes_name_col);
3958
3959 renderer = gtk_cell_renderer_text_new();
3960 g_object_set(renderer, "editable", TRUE, NULL);
3961 g_signal_connect(renderer, "edited", G_CALLBACK(_blendop_masks_all_name_edited), module);
3962 gtk_tree_view_column_pack_start(all_shapes_name_col, renderer, TRUE);
3963 gtk_tree_view_column_add_attribute(all_shapes_name_col, renderer, "markup", BLENDOP_MASKS_ALL_COL_MARKUP);
3964 gtk_tree_view_column_add_attribute(all_shapes_name_col, renderer, "sensitive", BLENDOP_MASKS_ALL_COL_SENSITIVE);
3965 g_object_set_data(G_OBJECT(bd->masks_treeview), "blendop-masks-name-renderer", renderer);
3966
3967 GtkTreeViewColumn *all_shapes_status_col = gtk_tree_view_column_new();
3968 gtk_tree_view_append_column(GTK_TREE_VIEW(bd->masks_treeview), all_shapes_status_col);
3969
3970 renderer = gtk_cell_renderer_text_new();
3971 g_object_set(renderer, "xalign", 1.0f, NULL);
3972 gtk_tree_view_column_pack_end(all_shapes_status_col, renderer, TRUE);
3973 gtk_tree_view_column_add_attribute(all_shapes_status_col, renderer, "markup",
3975 gtk_tree_view_column_add_attribute(all_shapes_status_col, renderer, "sensitive",
3977
3978 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(bd->masks_treeview));
3979 gtk_tree_selection_set_mode(selection, GTK_SELECTION_SINGLE);
3980 g_signal_connect(selection, "changed", G_CALLBACK(_blendop_masks_all_selection_changed), module);
3981 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(bd->masks_treeview), FALSE);
3982 g_signal_connect(bd->masks_treeview, "button-press-event",
3983 G_CALLBACK(_blendop_masks_all_button_pressed), module);
3984
3986 "plugins/darkroom/masks/all_list_height", DT_UI_RESIZE_DYNAMIC);
3987 gtk_widget_set_vexpand(bd->all_shapes_sw, TRUE);
3988
3989 // The two lists share the same slot; Edit selects which one is visible.
3990 bd->lists_stack = gtk_stack_new();
3991 gtk_stack_set_transition_type(GTK_STACK(bd->lists_stack), GTK_STACK_TRANSITION_TYPE_NONE);
3992 gtk_stack_set_homogeneous(GTK_STACK(bd->lists_stack), FALSE);
3993 gtk_stack_add_named(GTK_STACK(bd->lists_stack), bd->group_shapes_sw, "group");
3994 gtk_stack_add_named(GTK_STACK(bd->lists_stack), bd->all_shapes_sw, "all");
3995 gtk_box_pack_start(GTK_BOX(bd->lists_box), bd->lists_stack, TRUE, TRUE, 0);
3996
3997 // Default state:
3998 // - current-module tree is visible
3999 // - all-shapes tree is hidden
4000 // The toggle callback flips these two scrolled windows.
4001 g_signal_connect(bd->wire_shape_toggle, "toggled", G_CALLBACK(_blendop_masks_edit_list_toggle), module);
4002
4003 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(bd->wire_shape_toggle), FALSE);
4004 _blendop_masks_edit_list_toggle(GTK_TOGGLE_BUTTON(bd->wire_shape_toggle), module);
4005
4006 gtk_box_pack_start(GTK_BOX(bd->masks_box), bd->lists_box, TRUE, TRUE, 0);
4007
4009 (&bd->masks_cs,
4010 "plugins/darkroom/masks/wacom",
4011 _("Brush options"),
4012 GTK_BOX(bd->masks_box), GTK_PACK_END);
4013
4014 GtkWidget *brush_smoothing = dt_bauhaus_combobox_from_conf(darktable.bauhaus, DT_GUI_MODULE(NULL), "brush_smoothing");
4015 gtk_box_pack_start(GTK_BOX(bd->masks_cs.container), brush_smoothing, TRUE, TRUE, 0);
4016
4017 GtkWidget *pressure_mapping = dt_bauhaus_combobox_from_conf(darktable.bauhaus, DT_GUI_MODULE(NULL), "pressure_sensitivity");
4018 gtk_box_pack_start(GTK_BOX(bd->masks_cs.container), pressure_mapping, TRUE, TRUE, 0);
4019
4020 bd->masks_inited = 1;
4022 }
4023 else
4024 {
4025 gchar *module_name = dt_history_item_get_name(module);
4026 gchar *markup = g_markup_printf_escaped(
4027 _("<i>Drawn masking is disabled because the <b>%s</b> module manages drawn shapes internally.</i>"),
4028 module_name);
4029 GtkWidget *label = gtk_label_new(NULL);
4030 gtk_label_set_markup(GTK_LABEL(label), markup);
4031 gtk_label_set_xalign(GTK_LABEL(label), 0.0f);
4032 gtk_label_set_line_wrap(GTK_LABEL(label), TRUE);
4033 gtk_box_pack_start(GTK_BOX(bd->masks_box), label, FALSE, FALSE, 0);
4034 dt_free(markup);
4035 dt_free(module_name);
4036 }
4037}
4038
4044
4045static void _raster_combo_populate(GtkWidget *w, void *m)
4046{
4047 dt_iop_module_t *module = (dt_iop_module_t *)m;
4048 dt_iop_request_focus(module);
4049
4051
4052 raster_combo_entry_t *entry = (raster_combo_entry_t *)calloc(1, sizeof(raster_combo_entry_t));
4053 if(IS_NULL_PTR(entry)) return;
4054 dt_bauhaus_combobox_add_full(w, _("no mask used"), DT_BAUHAUS_COMBOBOX_ALIGN_RIGHT, entry, free, TRUE);
4055
4056 int i = 1;
4057
4058 for(GList* iter = darktable.develop->iop; iter; iter = g_list_next(iter))
4059 {
4060 dt_iop_module_t *iop = (dt_iop_module_t *)iter->data;
4061 if(iop == module)
4062 break;
4063
4064 GHashTableIter masks_iter;
4065 gpointer key, value;
4066
4067 g_hash_table_iter_init(&masks_iter, iop->raster_mask.source.masks);
4068 while(g_hash_table_iter_next(&masks_iter, &key, &value))
4069 {
4070 const int id = GPOINTER_TO_INT(key);
4071 const char *modulename = (char *)value;
4072 entry = (raster_combo_entry_t *)malloc(sizeof(raster_combo_entry_t));
4073 entry->module = iop;
4074 entry->id = id;
4076 if(iop == module->raster_mask.sink.source && module->raster_mask.sink.id == id)
4078 i++;
4079 }
4080 }
4081}
4082
4084{
4086
4087 // nothing to do
4088 if(entry->module == module->raster_mask.sink.source && entry->id == module->raster_mask.sink.id)
4089 return;
4090
4091 if(module->raster_mask.sink.source)
4092 {
4093 // we no longer use this one
4094 g_hash_table_remove(module->raster_mask.sink.source->raster_mask.source.users, module);
4095 }
4096
4097 module->raster_mask.sink.source = entry->module;
4098 module->raster_mask.sink.id = entry->id;
4099
4100 if(entry->module)
4101 {
4102 // The value is the provider-local mask id. dt_iop_is_raster_mask_used()
4103 // reads it to decide which side-band mask buffers the pixelpipe must
4104 // publish. g_hash_table_add() stores the consumer pointer as both key and
4105 // value, making an existing mask visible in the GUI but impossible to
4106 // retrieve reliably from the pipeline.
4107 g_hash_table_insert(entry->module->raster_mask.source.users, module,
4108 GINT_TO_POINTER(entry->id));
4109
4110 // update blend_params!
4111 memcpy(module->blend_params->raster_mask_source, entry->module->op, sizeof(module->blend_params->raster_mask_source));
4112 module->blend_params->raster_mask_instance = entry->module->multi_priority;
4113 module->blend_params->raster_mask_id = entry->id;
4114 }
4115 else
4116 {
4117 memset(module->blend_params->raster_mask_source, 0, sizeof(module->blend_params->raster_mask_source));
4118 module->blend_params->raster_mask_instance = 0;
4119 module->blend_params->raster_mask_id = 0;
4120 }
4121
4122 dt_dev_add_history_item(module->dev, module, TRUE, TRUE);
4124}
4125
4127{
4129 dt_develop_blend_params_t *bp = module->blend_params;
4130
4131 if(IS_NULL_PTR(bd) || !bd->masks_support || !bd->raster_inited) return;
4132
4133 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(bd->raster_polarity), bp->raster_mask_invert);
4134
4136}
4137
4138static void _raster_polarity_callback(GtkToggleButton *togglebutton, dt_iop_module_t *self)
4139{
4140 if(darktable.gui->reset) return;
4141
4143
4144 bp->raster_mask_invert = gtk_toggle_button_get_active(togglebutton);
4145
4148 dt_control_queue_redraw_widget(GTK_WIDGET(togglebutton));
4149}
4150
4151void dt_iop_gui_init_raster(GtkBox *blendw, dt_iop_module_t *module)
4152{
4154
4155 bd->raster_box = blendw;
4156
4157 /* create and add raster support if module supports it (it's coupled to masks at the moment) */
4158 if(bd->masks_support)
4159 {
4160 GtkWidget *hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, DT_GUI_BOX_SPACING);
4161
4165 dt_bauhaus_widget_set_label(bd->raster_combo, N_("raster mask"));
4166 dt_bauhaus_combobox_add(bd->raster_combo, _("no mask used"));
4167 g_signal_connect(G_OBJECT(bd->raster_combo), "value-changed",
4168 G_CALLBACK(_raster_value_changed_callback), module);
4170 gtk_box_pack_start(GTK_BOX(hbox), bd->raster_combo, TRUE, TRUE, 0);
4171
4173 dt_gui_add_class(bd->raster_polarity, "dt_ignore_fg_state");
4174 gtk_widget_set_tooltip_text(bd->raster_polarity, _("toggle polarity of raster mask"));
4175 g_signal_connect(G_OBJECT(bd->raster_polarity), "toggled", G_CALLBACK(_raster_polarity_callback), module);
4176 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(bd->raster_polarity), FALSE);
4177 gtk_box_pack_start(GTK_BOX(hbox), bd->raster_polarity, FALSE, FALSE, 0);
4178
4179 gtk_box_pack_start(GTK_BOX(bd->raster_box), GTK_WIDGET(hbox), FALSE, FALSE, 0);
4180
4181 bd->raster_inited = 1;
4182 }
4183}
4184
4186{
4187 if(IS_NULL_PTR(module->blend_data)) return;
4189
4192
4194 if(bd->timeout_handle)
4195 g_source_remove(bd->timeout_handle);
4196
4200
4201 dt_free(module->blend_data);
4202 module->blend_data = NULL;
4203}
4204
4205
4207{
4208 for(const dt_develop_name_value_t *bm = dt_develop_blend_mode_names; *bm->name; bm++)
4209 {
4210 if(bm->value == mode)
4211 {
4212 dt_bauhaus_combobox_add_full(combobox, g_dpgettext2(NULL, "blendmode", bm->name), DT_BAUHAUS_COMBOBOX_ALIGN_RIGHT, GUINT_TO_POINTER(bm->value), NULL, TRUE);
4213
4214 return TRUE;
4215 }
4216 }
4217
4218 return FALSE;
4219}
4220
4221static GtkWidget *_combobox_new_from_list(dt_iop_module_t *module, const gchar *label,
4222 const dt_develop_name_value_t *list, uint32_t *field, const gchar *tooltip)
4223{
4228
4229 if(field)
4231 dt_bauhaus_widget_set_label(combo, label);
4232 gtk_widget_set_tooltip_text(combo, tooltip);
4233 for(; *list->name; list++)
4235 GUINT_TO_POINTER(list->value), NULL, TRUE);
4236
4237 return combo;
4238}
4239
4240static void _notebook_append_full_width_page(GtkWidget *notebook, GtkWidget *page, const gchar *label)
4241{
4242 GtkWidget *tab = gtk_label_new(label);
4243 gtk_notebook_append_page(GTK_NOTEBOOK(notebook), page, tab);
4244 gtk_container_child_set(GTK_CONTAINER(notebook), page, "tab-expand", TRUE, "tab-fill", TRUE, NULL);
4245}
4246
4247static GtkWidget *_blendop_create_enable_toggle(dt_iop_module_t *module, const unsigned int mask_bit)
4248{
4249 const gboolean is_disable_toggle = mask_bit == DEVELOP_MASK_RASTER
4250 || mask_bit == DEVELOP_MASK_SHAPE
4251 || mask_bit == DEVELOP_MASK_PARAMETRIC;
4252 GtkWidget *toggle = gtk_check_button_new_with_label(is_disable_toggle ? _("Disable") : _("Enable"));
4253 g_object_set_data(G_OBJECT(toggle), "mask-bit", GUINT_TO_POINTER(mask_bit));
4254 g_signal_connect(G_OBJECT(toggle), "toggled", G_CALLBACK(_blendop_masks_mode_changed), module);
4255 return toggle;
4256}
4257
4259{
4260 if(IS_NULL_PTR(module) || !module->blend_data) return;
4261
4263 if(!GTK_IS_BUTTON(bd->top_enable)) return;
4264
4265 gchar *clean_name = delete_underscore(module->name());
4266 const char *multi_name = module->multi_name[0] ? module->multi_name : "0";
4267 gchar *multi_name_dup = ((g_strcmp0(multi_name, "0") == 0) || (g_strcmp0(multi_name, "") == 0)) ? g_strdup("") : g_strdup_printf(" (%s)", multi_name);
4268 GtkWidget *child = gtk_bin_get_child(GTK_BIN(bd->top_enable));
4269 gchar *label = g_markup_printf_escaped(_("Enable in <b>%s%s</b>"), clean_name, multi_name_dup);
4270 if(GTK_IS_LABEL(child))
4271 {
4272 gtk_label_set_markup(GTK_LABEL(child), label);
4273 gtk_label_set_line_wrap(GTK_LABEL(child), TRUE);
4274 gtk_label_set_xalign(GTK_LABEL(child), 0.0f);
4275 }
4276 dt_free(label);
4277 dt_free(clean_name);
4278 dt_free(multi_name_dup);
4279}
4280
4281static GtkWidget *_blendop_create_notebook_page(GtkWidget *notebook, const gchar *label,
4282 gchar *help_url, GtkWidget **content)
4283{
4284 GtkWidget *page = gtk_event_box_new();
4285 if(help_url) dt_gui_add_help_link(page, help_url);
4286 *content = gtk_box_new(GTK_ORIENTATION_VERTICAL, DT_GUI_BOX_SPACING);
4287 gtk_container_add(GTK_CONTAINER(page), *content);
4288 _notebook_append_full_width_page(notebook, page, label);
4289 return page;
4290}
4291
4292static GtkWidget *_blendop_create_toggle_page(GtkWidget *notebook, const gchar *label,
4293 gchar *help_url,
4294 dt_iop_module_t *module, const unsigned int mask_bit,
4295 GtkWidget **toggle, GtkWidget **content)
4296{
4297 GtkWidget *page = gtk_event_box_new();
4298 if(help_url) dt_gui_add_help_link(page, help_url);
4299 GtkWidget *vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, DT_GUI_BOX_SPACING);
4300 gtk_container_add(GTK_CONTAINER(page), vbox);
4301
4302 GtkWidget *header = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, DT_GUI_BOX_SPACING);
4303 *toggle = _blendop_create_enable_toggle(module, mask_bit);
4304 gtk_box_pack_start(GTK_BOX(header), *toggle, FALSE, FALSE, 0);
4305 gtk_box_pack_start(GTK_BOX(vbox), header, FALSE, FALSE, 0);
4306
4307 *content = gtk_box_new(GTK_ORIENTATION_VERTICAL, DT_GUI_BOX_SPACING);
4308 gtk_box_pack_start(GTK_BOX(vbox), *content, TRUE, TRUE, 0);
4309
4310 _notebook_append_full_width_page(notebook, page, label);
4311 return header;
4312}
4313
4314static void _blendop_toggle_button_set_active(GtkWidget *toggle, const gboolean enabled)
4315{
4316 if(GTK_IS_TOGGLE_BUTTON(toggle))
4317 {
4318 const unsigned int bit = GPOINTER_TO_UINT(g_object_get_data(G_OBJECT(toggle), "mask-bit"));
4319 const gboolean is_disable_toggle = bit == DEVELOP_MASK_RASTER
4320 || bit == DEVELOP_MASK_SHAPE
4321 || bit == DEVELOP_MASK_PARAMETRIC;
4322 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle), is_disable_toggle ? !enabled : enabled);
4323 }
4324}
4325
4326static void _blendop_sync_toggle_state(GtkWidget *toggle, const gboolean available,
4327 const gboolean enabled, GtkWidget *content)
4328{
4329 _blendop_toggle_button_set_active(toggle, enabled);
4330 if(GTK_IS_WIDGET(toggle))
4331 gtk_widget_set_sensitive(toggle, available);
4332 if(GTK_IS_WIDGET(content))
4333 gtk_widget_set_sensitive(content, available && enabled);
4334}
4335
4337{
4339
4340 if(!(module->flags() & IOP_FLAGS_SUPPORTS_BLENDING) || IS_NULL_PTR(bd)) return;
4341
4342 ++darktable.gui->reset;
4343
4344 // update color space from parameters
4346 switch(default_csp)
4347 {
4350 break;
4354 switch(module->blend_params->blend_cst)
4355 {
4359 bd->csp = module->blend_params->blend_cst;
4360 break;
4361 default:
4362 bd->csp = default_csp;
4363 break;
4364 }
4365 break;
4367 default:
4369 break;
4370 }
4371
4372 // (un)set the mask indicator
4374
4375 if(!bd->blending_box)
4376 {
4377 --darktable.gui->reset;
4378 return;
4379 }
4380
4381 // initialization of blending modes
4382 if(bd->csp != bd->blend_modes_csp)
4383 {
4385
4386 if(bd->csp == DEVELOP_BLEND_CS_LAB
4388 || bd->csp == DEVELOP_BLEND_CS_RAW )
4389 {
4406
4407 if(bd->csp == DEVELOP_BLEND_CS_LAB)
4408 {
4418 }
4419 else if(bd->csp == DEVELOP_BLEND_CS_RGB_DISPLAY)
4420 {
4431 }
4432 }
4433 else if(bd->csp == DEVELOP_BLEND_CS_RGB_SCENE)
4434 {
4449 }
4450 bd->blend_modes_csp = bd->csp;
4451 }
4452
4453 dt_develop_blend_mode_t blend_mode = module->blend_params->blend_mode & DEVELOP_BLEND_MODE_MASK;
4454 // module->blend_params->mask_mode
4456 {
4457 // add deprecated blend mode
4458 if(!_add_blendmode_combo(bd->blend_modes_combo, blend_mode))
4459 {
4460 // should never happen: unknown blend mode
4461 dt_control_log("unknown blend mode '%d' in module '%s'", blend_mode, module->op);
4462 module->blend_params->blend_mode = DEVELOP_BLEND_NORMAL2;
4463 blend_mode = DEVELOP_BLEND_NORMAL2;
4464 }
4465
4467 }
4468
4469 gboolean blend_mode_reversed = (module->blend_params->blend_mode & DEVELOP_BLEND_REVERSE) == DEVELOP_BLEND_REVERSE;
4471
4473 const gboolean show_param = (bd->blend_modes_csp == DEVELOP_BLEND_CS_RGB_SCENE) &&
4475 if(!show_param)
4476 {
4477 module->blend_params->blend_parameter = 0.0f;
4479 }
4480 gtk_widget_set_visible(bd->blend_mode_parameter_slider, show_param);
4481
4494
4495 /* reset all alternative display modes for blendif */
4496 memset(bd->altmode, 0, sizeof(bd->altmode));
4497
4498 // force the visibility of output channels if they contain some setting
4501
4503 dt_masks_iop_update(module);
4505
4506 /* sync page states from mask mode */
4507 uint32_t mask_mode = module->blend_params->mask_mode;
4508 const gboolean top_enabled = (mask_mode & DEVELOP_MASK_ENABLED) != 0;
4509 const gboolean masks_enabled = (mask_mode & DEVELOP_MASK_SHAPE) != 0;
4510 const gboolean raster_enabled = (mask_mode & DEVELOP_MASK_RASTER) != 0;
4511 const gboolean blendif_enabled = (mask_mode & DEVELOP_MASK_PARAMETRIC) != 0;
4512 const gboolean bottom_enabled = top_enabled && ((bd->masks_inited && masks_enabled)
4513 || (bd->raster_inited && raster_enabled)
4514 || (bd->blendif_inited && blendif_enabled));
4515
4518
4519 if(GTK_IS_WIDGET(bd->blending_box))
4520 {
4521 gtk_widget_set_sensitive(bd->blending_box, top_enabled);
4522 // Note: the showmask toggle is outside the blending box.
4523 gtk_widget_set_sensitive(bd->showmask, top_enabled);
4524 }
4525
4529
4530 if(bd->blendif_inited && !IS_NULL_PTR(bd->channel))
4531 {
4532 /* The parametric page may have made the whole content insensitive before
4533 * `_blendop_blendif_update_tab()` restored the channel-local state.
4534 * Reapply the final contract after the page toggle is synchronized:
4535 * boost factor is editable only for an enabled parametric mask and a
4536 * channel whose descriptor explicitly supports boosting. */
4537 gtk_widget_set_sensitive(
4539 blendif_enabled && bd->channel[bd->tab].boost_factor_enabled);
4540 }
4541
4542 if(bd->blendif_inited && blendif_enabled)
4543 {
4544 gtk_widget_hide(GTK_WIDGET(bd->masks_invert_combo));
4545 gtk_widget_show(GTK_WIDGET(bd->masks_combine_combo));
4546 }
4547 else
4548 {
4549 gtk_widget_show(GTK_WIDGET(bd->masks_invert_combo));
4550 gtk_widget_hide(GTK_WIDGET(bd->masks_combine_combo));
4551 }
4552
4553 /*
4554 * if this iop is operating in raw space, it has only 1 channel per pixel,
4555 * thus there is no alpha channel where we would normally store mask
4556 * that would get displayed if following button have been pressed.
4557 */
4558 if(module->blend_colorspace(module, NULL, NULL) == IOP_CS_RAW)
4559 {
4560 module->request_mask_display = DT_DEV_PIXELPIPE_DISPLAY_NONE;
4563 gtk_widget_hide(GTK_WIDGET(bd->showmask));
4564
4565 // disable also guided filters on RAW based color space
4566 gtk_widget_set_sensitive(bd->masks_feathering_guide_combo, FALSE);
4567 gtk_widget_hide(bd->masks_feathering_guide_combo);
4568 gtk_widget_set_sensitive(bd->feathering_radius_slider, FALSE);
4569 gtk_widget_hide(bd->feathering_radius_slider);
4570 gtk_widget_set_sensitive(bd->brightness_slider, FALSE);
4571 gtk_widget_hide(bd->brightness_slider);
4572 gtk_widget_set_sensitive(bd->contrast_slider, FALSE);
4573 gtk_widget_hide(bd->contrast_slider);
4574 gtk_widget_set_sensitive(bd->details_slider, FALSE);
4575 gtk_widget_hide(bd->details_slider);
4576 }
4577 else
4578 {
4579 gtk_widget_show(GTK_WIDGET(bd->showmask));
4580 gtk_widget_set_sensitive(bd->masks_feathering_guide_combo, TRUE);
4581 gtk_widget_show(bd->masks_feathering_guide_combo);
4582 gtk_widget_set_sensitive(bd->feathering_radius_slider, TRUE);
4583 gtk_widget_show(bd->feathering_radius_slider);
4584 gtk_widget_set_sensitive(bd->brightness_slider, TRUE);
4585 gtk_widget_show(bd->brightness_slider);
4586 gtk_widget_set_sensitive(bd->contrast_slider, TRUE);
4587 gtk_widget_show(bd->contrast_slider);
4588 /* show details slider only if it was used in an old edit (non-zero) */
4589 const gboolean show_details = (module->blend_params->details != 0.0f);
4590 gtk_widget_set_visible(bd->details_slider, show_details);
4591 }
4592
4593 if(!bottom_enabled)
4594 {
4595 module->request_mask_display = DT_DEV_PIXELPIPE_DISPLAY_NONE;
4598 }
4599
4600 if(bd->masks_inited && !masks_enabled)
4601 {
4604 }
4605
4606 if(bd->masks_support && !masks_enabled)
4607 {
4609 }
4610
4611 if(bd->blendif_inited && !blendif_enabled)
4612 {
4614 }
4615
4617
4618 // Re-evaluate header state after notebook toggle synchronization.
4620
4621 --darktable.gui->reset;
4622}
4623
4625{
4626 // FIXME : there are more than 3 functions getting clever about how to setup module->request_mask_display depending on user input.
4627 // These should all use an uniform setter function.
4628 //
4629 // The lack of setter implies we don't guaranty that only 1 module can request mask display at a time.
4630 // The consequence is pipeline needs to check if module->request_mask_display AND module == dev->gui_module,
4631 // but the global pipe->mask_display is set from *_blend_process() at runtime, so it's a pipe property
4632 // that changes over the pipe lifecycle.
4633 //
4634 // This is a self-feeding loop of madness because it ties the pipeline to GUI states
4635 // (but not all pipes are connected to a GUI, so you need to cover all cases all the time and don't forget to test everything),
4636 // and because the pipeline is executed recursively from the end, but pipe->mask_display is set in the middle,
4637 // when it reaches the process() method of the module capturing mask preview, so you don't have this info when
4638 // planning for pipeline execution.
4639 // And you need to plan for mask preview ahead in pipe because mask preview needs to work
4640 // without using the pixelpipe cache, at least between the module requiring mask preview and gamma.c, which will actually render
4641 // the preview at the far end of the pipe.
4642 // So the not-so-clever workaround inherited from darktable was to flush all cache lines when requesting mask preview,
4643 // which flushed lines that could be reused later and were only temporarily not needed.
4644
4645 if(darktable.gui->reset) return;
4646 if(IS_NULL_PTR(module)) return;
4647
4648 const int has_mask_display = module->request_mask_display & (DT_DEV_PIXELPIPE_DISPLAY_MASK | DT_DEV_PIXELPIPE_DISPLAY_CHANNEL);
4649
4650 if((module->flags() & IOP_FLAGS_SUPPORTS_BLENDING) && module->blend_data)
4651 {
4653 if(bd->showmask)
4655 module->request_mask_display = DT_DEV_PIXELPIPE_DISPLAY_NONE;
4658
4659 // (re)set the header mask indicator too
4660 if(bd->masks_support && bd->masks_edit)
4661 {
4662 // unselect all tools
4665
4667 }
4668
4671 if(bd->timeout_handle)
4672 {
4673 // purge any remaining timeout handlers
4674 g_source_remove(bd->timeout_handle);
4675 bd->timeout_handle = 0;
4676 }
4678
4679 // reprocess main center image if needed
4680 if (has_mask_display)
4682 }
4683}
4684
4686{
4687 if(IS_NULL_PTR(module)) return;
4688 dt_iop_gui_blend_data_t *bd = module->blend_data;
4689 if(IS_NULL_PTR(bd) || !bd->blendif_support || !bd->blendif_inited) return;
4691}
4692
4694{
4695 if(IS_NULL_PTR(module) || !module->blend_data) return;
4696
4698 if(IS_NULL_PTR(bd->blending_box)) return;
4700 if(bd->timeout_handle)
4701 {
4702 g_source_remove(bd->timeout_handle);
4703 bd->timeout_handle = 0;
4704 }
4707
4708 if(bd->masks_ic_inverse) g_object_unref(bd->masks_ic_inverse);
4709 if(bd->masks_ic_union) g_object_unref(bd->masks_ic_union);
4710 if(bd->masks_ic_intersection) g_object_unref(bd->masks_ic_intersection);
4711 if(bd->masks_ic_difference) g_object_unref(bd->masks_ic_difference);
4712 if(bd->masks_ic_exclusion) g_object_unref(bd->masks_ic_exclusion);
4713 if(bd->group_shapes_store) g_object_unref(bd->group_shapes_store);
4714 if(bd->all_shapes_store) g_object_unref(bd->all_shapes_store);
4715
4716 gtk_widget_destroy(bd->blending_box);
4717
4718 bd->blending_box = NULL;
4719 bd->blending_notebook = NULL;
4720 bd->top_enable = NULL;
4721 bd->masks_enable = NULL;
4722 bd->raster_enable = NULL;
4723 bd->blendif_enable = NULL;
4724 bd->blending_box = NULL;
4725 bd->masks_content = NULL;
4726 bd->raster_content = NULL;
4727 bd->blendif_content = NULL;
4728 bd->contours_content = NULL;
4729 bd->blendif_box = NULL;
4730 bd->masks_box = NULL;
4731 bd->raster_box = NULL;
4732 bd->colorpicker = NULL;
4733 bd->colorpicker_set_values = NULL;
4734 memset(bd->filter, 0, sizeof(bd->filter));
4735 bd->showmask = NULL;
4736 bd->masks_combine_combo = NULL;
4737 bd->blend_modes_combo = NULL;
4738 bd->blend_modes_blend_order = NULL;
4739 bd->blend_mode_parameter_slider = NULL;
4740 bd->masks_invert_combo = NULL;
4741 bd->opacity_slider = NULL;
4743 bd->feathering_radius_slider = NULL;
4744 bd->blur_radius_slider = NULL;
4745 bd->contrast_slider = NULL;
4746 bd->brightness_slider = NULL;
4747 bd->channel_boost_factor_slider = NULL;
4748 bd->details_slider = NULL;
4749 bd->masks_combo = NULL;
4750 memset(bd->masks_shapes, 0, sizeof(bd->masks_shapes));
4751 bd->masks_edit = NULL;
4752 bd->group_shapes_label = NULL;
4753 bd->masks_polarity = NULL;
4754 bd->wire_shape_toggle = NULL;
4755 bd->masks_treeview = NULL;
4756 bd->masks_group_treeview = NULL;
4757 bd->group_shapes_store = NULL;
4758 bd->group_shapes_col = NULL;
4759 bd->group_unlink_col = NULL;
4760 bd->group_delete_col = NULL;
4761 bd->all_shapes_store = NULL;
4762 bd->group_shapes_sw = NULL;
4763 bd->all_shapes_col = NULL;
4764 bd->all_shapes_sw = NULL;
4765 bd->lists_stack = NULL;
4766 bd->all_shapes_buttons = NULL;
4767 bd->lists_box = NULL;
4768 bd->masks_ic_inverse = NULL;
4769 bd->masks_ic_union = NULL;
4770 bd->masks_ic_intersection = NULL;
4771 bd->masks_ic_difference = NULL;
4772 bd->masks_ic_exclusion = NULL;
4773 bd->raster_combo = NULL;
4774 bd->raster_polarity = NULL;
4776 memset(bd->picker_set_values_box, 0, sizeof(bd->picker_set_values_box));
4778 bd->channel_tabs = NULL;
4779 bd->blendif_inited = 0;
4780 bd->masks_inited = 0;
4781 bd->raster_inited = 0;
4784 module->fusion_slider = NULL;
4785}
4786
4788{
4789 if(IS_NULL_PTR(container) || !GTK_IS_WIDGET(container)) return;
4790 if(!(module->flags() & IOP_FLAGS_SUPPORTS_BLENDING)) return;
4791 if(IS_NULL_PTR(module->blend_data)) return;
4792
4794 if(bd->blending_box) return;
4795
4796 ++darktable.gui->reset;
4797
4798 /* NOTE: we attach 2 modules to the parent container:
4799 1. the on/off global switch,
4800 2. the box containing all other options (recursively nested).
4801 This is just a trick so we can hide/insensitize the whole content
4802 in one shot, on main disabling, except for the main switch.
4803 */
4804
4805 /* Global ON/OFF switch + show/hide toggle */
4806 GtkWidget *display_box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, DT_GUI_BOX_SPACING);
4807 dt_gui_add_help_link(display_box, dt_get_help_url("masks_blending"));
4808 gtk_widget_set_name(display_box, "blending-main-switch");
4809
4812 gtk_box_pack_start(GTK_BOX(display_box), bd->top_enable, FALSE, FALSE, 0);
4813
4814 bd->showmask = dt_iop_togglebutton_new_no_register(module, "blend`tools", N_("display mask and/or color channel"), NULL,
4816 FALSE, 0, 0, dtgtk_cairo_paint_showmask, display_box);
4817 gtk_widget_set_tooltip_text(bd->showmask, _("display mask and/or color channel. ctrl+click to display mask, "
4818 "shift+click to display channel. hover over parametric mask slider to "
4819 "select channel for display"));
4820
4821 gtk_box_pack_start(GTK_BOX(container), display_box, FALSE, FALSE, 0);
4822 gtk_widget_show_all(display_box);
4823
4824 /* Main widget container */
4825 bd->blending_box = gtk_box_new(GTK_ORIENTATION_VERTICAL, DT_GUI_BOX_SPACING);
4826 gtk_box_pack_start(GTK_BOX(container), bd->blending_box, TRUE, TRUE, 0);
4827 gtk_widget_set_name(bd->blending_box, "blending-box");
4828
4829 /* Blend mode */
4834 dt_bauhaus_widget_set_label(bd->blend_modes_combo, N_("Blend mode"));
4835 g_signal_connect(G_OBJECT(bd->blend_modes_combo), "value-changed",
4836 G_CALLBACK(_blendop_blend_mode_callback), bd);
4837 dt_gui_add_help_link(GTK_WIDGET(bd->blend_modes_combo), dt_get_help_url("masks_blending_op"));
4838 gtk_box_pack_start(GTK_BOX(bd->blending_box), bd->blend_modes_combo, TRUE, TRUE, 0);
4839
4840 /* Compositing combobox: choose compositing order */
4844 GINT_TO_POINTER(0), NULL, TRUE);
4846 GINT_TO_POINTER(1), NULL, TRUE);
4849 gtk_widget_set_tooltip_text(bd->blend_modes_blend_order, _("choose compositing order: which layer is placed on top"));
4850 g_signal_connect(G_OBJECT(bd->blend_modes_blend_order), "value-changed",
4851 G_CALLBACK(_blendop_compositing_changed), bd);
4852 gtk_box_pack_start(GTK_BOX(bd->blending_box), bd->blend_modes_blend_order, TRUE, TRUE, 0);
4853
4854 /* Optional fulcrum parameter for blend modes supporting it */
4863 gtk_widget_set_tooltip_text(bd->blend_mode_parameter_slider, _("adjust the fulcrum used by some blending"
4864 " operations"));
4865 g_object_set_data(G_OBJECT(bd->blend_mode_parameter_slider), "dt-blendop-header-update", GINT_TO_POINTER(TRUE));
4866 gtk_widget_set_visible(bd->blend_mode_parameter_slider, FALSE);
4867 gtk_box_pack_start(GTK_BOX(bd->blending_box), bd->blend_mode_parameter_slider, TRUE, TRUE, 0);
4868
4869 /* mask combine control: how to combine drawn/parametric masks */
4870 bd->masks_combine_combo = _combobox_new_from_list(module, _("Combine drawn & parametric"), dt_develop_combine_masks_names, NULL,
4871 _("how to combine drawn and parametric masks"));
4872 g_signal_connect(G_OBJECT(bd->masks_combine_combo), "value-changed",
4873 G_CALLBACK(_blendop_masks_combine_callback), bd);
4874 dt_gui_add_help_link(GTK_WIDGET(bd->masks_combine_combo), dt_get_help_url("masks_combined"));
4875 gtk_box_pack_start(GTK_BOX(bd->blending_box), GTK_WIDGET(bd->masks_combine_combo), TRUE, TRUE, 0);
4876
4877 /* mask invert control: single checkbox to invert masks */
4878 bd->masks_invert_combo = gtk_check_button_new_with_label(_("Invert mask"));
4879 gtk_widget_set_tooltip_text(bd->masks_invert_combo, _("invert the resulting mask (apply mask in inverted mode)"));
4880 g_signal_connect(G_OBJECT(bd->masks_invert_combo), "toggled",
4881 G_CALLBACK(_blendop_masks_invert_toggled), bd);
4882 gtk_box_pack_start(GTK_BOX(bd->blending_box), GTK_WIDGET(bd->masks_invert_combo), TRUE, TRUE, 0);
4883
4884 /* Global mask output opacity */
4885 bd->opacity_slider = dt_bauhaus_slider_new_with_range(darktable.bauhaus, DT_GUI_MODULE(module), 0.0, 100.0, 0, 100.0, 0);
4890 dt_bauhaus_widget_set_label(bd->opacity_slider, N_("opacity"));
4892 module->fusion_slider = bd->opacity_slider;
4893 gtk_widget_set_tooltip_text(bd->opacity_slider, _("set the opacity of the blending"));
4894 g_object_set_data(G_OBJECT(bd->opacity_slider), "dt-blendop-header-update", GINT_TO_POINTER(TRUE));
4895 gtk_box_pack_start(GTK_BOX(bd->blending_box), bd->opacity_slider, TRUE, TRUE, 0);
4896
4897 bd->details_slider = dt_bauhaus_slider_new_with_range(darktable.bauhaus, DT_GUI_MODULE(module), -1.0f, 1.0f, 0, 0.0f, 2);
4901 dt_bauhaus_widget_set_label(bd->details_slider, N_("details threshold"));
4903 gtk_widget_set_tooltip_text(bd->details_slider, _("adjust the threshold for the details mask, "
4904 "\npositive values selects areas with strong details, "
4905 "\nnegative values select flat areas"));
4906 g_signal_connect(G_OBJECT(bd->details_slider), "value-changed", G_CALLBACK(_blendop_blendif_details_callback), bd);
4907 gtk_widget_hide(bd->details_slider);
4908
4914 dt_bauhaus_widget_set_label(bd->blur_radius_slider, N_("blurring radius"));
4916 gtk_widget_set_tooltip_text(bd->blur_radius_slider, _("radius for gaussian blur of blend mask"));
4917 g_object_set_data(G_OBJECT(bd->blur_radius_slider), "dt-blendop-header-update", GINT_TO_POINTER(TRUE));
4918
4921 _("choose to guide mask by input or output image and"
4922 "\nchoose to apply feathering before or after mask blur"));
4923 g_object_set_data(G_OBJECT(bd->masks_feathering_guide_combo), "dt-blendop-header-update", GINT_TO_POINTER(TRUE));
4924
4930 dt_bauhaus_widget_set_label(bd->feathering_radius_slider, N_("feathering radius"));
4932 gtk_widget_set_tooltip_text(bd->feathering_radius_slider, _("spatial radius of feathering"));
4933 g_object_set_data(G_OBJECT(bd->feathering_radius_slider), "dt-blendop-header-update", GINT_TO_POINTER(TRUE));
4934
4940 dt_bauhaus_widget_set_label(bd->brightness_slider, N_("Feathering mask opacity"));
4942 gtk_widget_set_tooltip_text(bd->brightness_slider, _("shifts and tilts the tone curve of the blend mask to adjust its "
4943 "brightness without affecting fully transparent/fully opaque "
4944 "regions"));
4945 g_object_set_data(G_OBJECT(bd->brightness_slider), "dt-blendop-header-update", GINT_TO_POINTER(TRUE));
4946
4952 dt_bauhaus_widget_set_label(bd->contrast_slider, N_("Feathering mask contrast"));
4954 gtk_widget_set_tooltip_text(bd->contrast_slider, _("gives the tone curve of the blend mask an s-like shape to "
4955 "adjust its contrast"));
4956 g_object_set_data(G_OBJECT(bd->contrast_slider), "dt-blendop-header-update", GINT_TO_POINTER(TRUE));
4957
4958 /* separator between global blending options and the masking sub-modes notebook */
4959 GtkWidget *blend_separator = gtk_separator_new(GTK_ORIENTATION_HORIZONTAL);
4960 gtk_widget_set_name(blend_separator, "blending-separator");
4961 gtk_box_pack_start(GTK_BOX(bd->blending_box), blend_separator, FALSE, FALSE, 0);
4962
4963 GtkWidget *presets_button = dtgtk_button_new(dtgtk_cairo_paint_presets, 0, NULL);
4964 gtk_widget_set_tooltip_text(presets_button, _("blending options"));
4965 if(bd->blendif_support)
4966 g_signal_connect(G_OBJECT(presets_button), "button-press-event", G_CALLBACK(_blendif_options_callback), module);
4967 else
4968 gtk_widget_set_sensitive(GTK_WIDGET(presets_button), FALSE);
4969
4970 /* Blending submodes notebook */
4971 bd->blending_notebook = gtk_notebook_new();
4972 gtk_notebook_set_scrollable(GTK_NOTEBOOK(bd->blending_notebook), TRUE);
4973 gtk_notebook_set_action_widget(GTK_NOTEBOOK(bd->blending_notebook), presets_button, GTK_PACK_END);
4974 gtk_widget_show(presets_button);
4975 gtk_box_pack_start(GTK_BOX(bd->blending_box), bd->blending_notebook, TRUE, TRUE, 0);
4976
4977 /* Raster page */
4978 _blendop_create_toggle_page(bd->blending_notebook, _("Raster"), dt_get_help_url("masks_raster"),
4980 dt_iop_gui_init_raster(GTK_BOX(bd->raster_content), module);
4981
4982 /* Drawn page */
4983 _blendop_create_toggle_page(bd->blending_notebook, _("Drawn"), dt_get_help_url("masks_drawn"),
4984 module, DEVELOP_MASK_SHAPE, &bd->masks_enable, &bd->masks_content);
4985 dt_iop_gui_init_masks(GTK_BOX(bd->masks_content), module);
4986
4987 /* Parametric page */
4988 GtkWidget *blendif_header = _blendop_create_toggle_page(bd->blending_notebook, _("Parametric"),
4989 dt_get_help_url("masks_parametric"),
4991 &bd->blendif_content);
4992 dt_iop_gui_init_blendif(GTK_BOX(bd->blendif_content), module, blendif_header);
4993
4994 /* Contours page */
4995 _blendop_create_notebook_page(bd->blending_notebook, _("Contours"), dt_get_help_url("masks_refinement"), &bd->contours_content);
4996 dt_iop_gui_init_contours(GTK_BOX(bd->contours_content), module);
4997
4998 /* Init all GUI states from module params */
4999 const unsigned int mask_mode = module->blend_params->mask_mode;
5004
5005 gtk_widget_show_all(GTK_WIDGET(bd->blending_box));
5006 // show_all overrides the visibility the collapsible section set at init; re-apply config state.
5008 gtk_widget_set_sensitive(bd->blending_box, (mask_mode & DEVELOP_MASK_ENABLED) != 0);
5009
5010 g_signal_connect(G_OBJECT(bd->blending_notebook), "switch_page", G_CALLBACK(_blendop_blending_notebook_switch), bd);
5011 const int page_count = gtk_notebook_get_n_pages(GTK_NOTEBOOK(bd->blending_notebook));
5012 if(page_count > 0)
5013 {
5014 // Restore the last-used tab after the notebook has been fully realized.
5016 page = CLAMP(page, 0, page_count - 1);
5017 gtk_notebook_set_current_page(GTK_NOTEBOOK(bd->blending_notebook), page);
5018 }
5019
5020 --darktable.gui->reset;
5021}
5022
5024{
5025 if(!(module->flags() & IOP_FLAGS_SUPPORTS_BLENDING) || module->blend_data) return;
5026
5027 module->blend_data = g_malloc0(sizeof(dt_iop_gui_blend_data_t));
5029
5030 bd->module = module;
5034
5035 const dt_iop_colorspace_type_t cst = module->blend_colorspace(module, NULL, NULL);
5037 bd->masks_support = !(module->flags() & IOP_FLAGS_NO_MASKS);
5038
5039 dt_pthread_mutex_init(&bd->lock, NULL);
5042
5043 if(bd->masks_support)
5044 {
5046 G_CALLBACK(_blendop_masks_handler_callback), module);
5047 }
5048}
5049
5050// clang-format off
5051// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
5052// vim: shiftwidth=2 expandtab tabstop=2 cindent
5053// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
5054// clang-format on
#define TRUE
Definition ashift_lsd.c:162
#define FALSE
Definition ashift_lsd.c:158
uint32_t container(dt_lib_module_t *self)
#define m
Definition basecurve.c:278
void dt_bauhaus_slider_set_soft_range(GtkWidget *widget, float soft_min, float soft_max)
Definition bauhaus.c:1647
void dt_bauhaus_combobox_clear(GtkWidget *widget)
Definition bauhaus.c:2189
float dt_bauhaus_slider_get(GtkWidget *widget)
Definition bauhaus.c:3483
gboolean dt_bauhaus_combobox_set_from_value(GtkWidget *widget, int value)
Definition bauhaus.c:2330
void dt_bauhaus_widget_set_field(GtkWidget *widget, gpointer field, dt_introspection_type_t field_type)
Definition bauhaus.c:1710
GtkWidget * dt_bauhaus_combobox_from_conf(dt_bauhaus_t *bh, dt_gui_module_t *self, const char *confkey)
Definition bauhaus.c:1868
void dt_bauhaus_disable_module_list(GtkWidget *widget)
Definition bauhaus.c:3948
void dt_bauhaus_set_use_default_callback(GtkWidget *widget)
Tell the widget to use the globally-defined default callback in the bauhaus structure This callback n...
Definition bauhaus.c:3954
void dt_bauhaus_combobox_add_full(GtkWidget *widget, const char *text, dt_bauhaus_combobox_alignment_t align, gpointer data, void(free_func)(void *data), gboolean sensitive)
Definition bauhaus.c:2038
gpointer dt_bauhaus_combobox_get_data(GtkWidget *widget)
Definition bauhaus.c:2179
void dt_bauhaus_combobox_add_populate_fct(GtkWidget *widget, void(*fct)(GtkWidget *w, void *module))
Definition bauhaus.c:2002
void dt_bauhaus_disable_accels(GtkWidget *widget)
Definition bauhaus.c:3942
void dt_bauhaus_slider_set(GtkWidget *widget, float pos)
Definition bauhaus.c:3506
void dt_bauhaus_combobox_set(GtkWidget *widget, const int pos)
Definition bauhaus.c:2301
void dt_bauhaus_widget_set_label(GtkWidget *widget, const char *label)
Definition bauhaus.c:1653
GtkWidget * dt_bauhaus_slider_new_with_range(dt_bauhaus_t *bh, dt_gui_module_t *self, float min, float max, float step, float defval, int digits)
Definition bauhaus.c:1780
GtkWidget * dt_bauhaus_combobox_new(dt_bauhaus_t *bh, dt_gui_module_t *self)
Definition bauhaus.c:1842
void dt_bauhaus_slider_set_format(GtkWidget *widget, const char *format)
Definition bauhaus.c:3598
void dt_bauhaus_combobox_add(GtkWidget *widget, const char *text)
Definition bauhaus.c:2016
@ DT_BAUHAUS_COMBOBOX_ALIGN_RIGHT
Definition bauhaus.h:125
dt_develop_blend_colorspace_t dt_develop_blend_default_module_blend_colorspace(dt_iop_module_t *module)
Definition blend.c:135
void dt_develop_blend_init_blendif_parameters(dt_develop_blend_params_t *blend_params, dt_develop_blend_colorspace_t cst)
Definition blend.c:165
gboolean blend_color_picker_apply(dt_iop_module_t *module, GtkWidget *picker, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
@ DEVELOP_MASK_GUIDE_OUT_BEFORE_BLUR
Definition blend.h:138
@ DEVELOP_MASK_GUIDE_IN_AFTER_BLUR
Definition blend.h:139
@ DEVELOP_MASK_GUIDE_OUT_AFTER_BLUR
Definition blend.h:140
@ DEVELOP_MASK_GUIDE_IN_BEFORE_BLUR
Definition blend.h:137
dt_develop_blend_colorspace_t
Definition blend.h:55
@ DEVELOP_BLEND_CS_NONE
Definition blend.h:56
@ DEVELOP_BLEND_CS_LAB
Definition blend.h:58
@ DEVELOP_BLEND_CS_RGB_SCENE
Definition blend.h:60
@ DEVELOP_BLEND_CS_RGB_DISPLAY
Definition blend.h:59
@ DEVELOP_BLEND_CS_RAW
Definition blend.h:57
@ DEVELOP_COMBINE_INV
Definition blend.h:125
@ DEVELOP_COMBINE_NORM
Definition blend.h:124
@ DEVELOP_COMBINE_INCL
Definition blend.h:127
@ DEVELOP_COMBINE_MASKS_POS
Definition blend.h:128
@ DEVELOP_COMBINE_NORM_INCL
Definition blend.h:130
@ DEVELOP_COMBINE_INV_INCL
Definition blend.h:132
@ DEVELOP_COMBINE_INV_EXCL
Definition blend.h:131
@ DEVELOP_COMBINE_NORM_EXCL
Definition blend.h:129
dt_develop_blendif_channels_t
Definition blend.h:144
@ DEVELOP_BLENDIF_GRAY_out
Definition blend.h:158
@ DEVELOP_BLENDIF_hz_in
Definition blend.h:179
@ DEVELOP_BLENDIF_Cz_in
Definition blend.h:178
@ DEVELOP_BLENDIF_S_out
Definition blend.h:174
@ DEVELOP_BLENDIF_RED_in
Definition blend.h:154
@ DEVELOP_BLENDIF_C_out
Definition blend.h:166
@ DEVELOP_BLENDIF_A_in
Definition blend.h:146
@ DEVELOP_BLENDIF_C_in
Definition blend.h:163
@ DEVELOP_BLENDIF_l_in
Definition blend.h:171
@ DEVELOP_BLENDIF_L_out
Definition blend.h:149
@ DEVELOP_BLENDIF_GREEN_in
Definition blend.h:155
@ DEVELOP_BLENDIF_h_in
Definition blend.h:164
@ DEVELOP_BLENDIF_H_in
Definition blend.h:169
@ DEVELOP_BLENDIF_RED_out
Definition blend.h:159
@ DEVELOP_BLENDIF_Lab_MASK
Definition blend.h:192
@ DEVELOP_BLENDIF_Jz_out
Definition blend.h:181
@ DEVELOP_BLENDIF_B_in
Definition blend.h:147
@ DEVELOP_BLENDIF_Cz_out
Definition blend.h:182
@ DEVELOP_BLENDIF_Jz_in
Definition blend.h:177
@ DEVELOP_BLENDIF_H_out
Definition blend.h:173
@ DEVELOP_BLENDIF_A_out
Definition blend.h:150
@ DEVELOP_BLENDIF_B_out
Definition blend.h:151
@ DEVELOP_BLENDIF_h_out
Definition blend.h:167
@ DEVELOP_BLENDIF_OUTPUT_MASK
Definition blend.h:194
@ DEVELOP_BLENDIF_BLUE_in
Definition blend.h:156
@ DEVELOP_BLENDIF_L_in
Definition blend.h:145
@ DEVELOP_BLENDIF_hz_out
Definition blend.h:183
@ DEVELOP_BLENDIF_BLUE_out
Definition blend.h:161
@ DEVELOP_BLENDIF_RGB_MASK
Definition blend.h:193
@ DEVELOP_BLENDIF_S_in
Definition blend.h:170
@ DEVELOP_BLENDIF_GRAY_in
Definition blend.h:153
@ DEVELOP_BLENDIF_GREEN_out
Definition blend.h:160
@ DEVELOP_BLENDIF_l_out
Definition blend.h:175
dt_develop_blend_mode_t
Definition blend.h:64
@ DEVELOP_BLEND_LIGHTEN
Definition blend.h:67
@ DEVELOP_BLEND_COLOR
Definition blend.h:84
@ DEVELOP_BLEND_CHROMATICITY
Definition blend.h:82
@ DEVELOP_BLEND_DIFFERENCE
Definition blend.h:73
@ DEVELOP_BLEND_RGB_B
Definition blend.h:100
@ DEVELOP_BLEND_LIGHTNESS
Definition blend.h:81
@ DEVELOP_BLEND_BOUNDED
Definition blend.h:90
@ DEVELOP_BLEND_SUBTRACT
Definition blend.h:72
@ DEVELOP_BLEND_MODE_MASK
Definition blend.h:109
@ DEVELOP_BLEND_DIVIDE_INVERSE
Definition blend.h:104
@ DEVELOP_BLEND_NORMAL2
Definition blend.h:89
@ DEVELOP_BLEND_HARDLIGHT
Definition blend.h:77
@ DEVELOP_BLEND_HUE
Definition blend.h:83
@ DEVELOP_BLEND_OVERLAY
Definition blend.h:75
@ DEVELOP_BLEND_RGB_R
Definition blend.h:98
@ DEVELOP_BLEND_LAB_A
Definition blend.h:96
@ DEVELOP_BLEND_LAB_COLOR
Definition blend.h:92
@ DEVELOP_BLEND_REVERSE
Definition blend.h:108
@ DEVELOP_BLEND_AVERAGE
Definition blend.h:70
@ DEVELOP_BLEND_DIVIDE
Definition blend.h:103
@ DEVELOP_BLEND_HSV_COLOR
Definition blend.h:94
@ DEVELOP_BLEND_MULTIPLY
Definition blend.h:69
@ DEVELOP_BLEND_SCREEN
Definition blend.h:74
@ DEVELOP_BLEND_HARMONIC_MEAN
Definition blend.h:106
@ DEVELOP_BLEND_PINLIGHT
Definition blend.h:80
@ DEVELOP_BLEND_HSV_VALUE
Definition blend.h:93
@ DEVELOP_BLEND_LAB_L
Definition blend.h:95
@ DEVELOP_BLEND_LINEARLIGHT
Definition blend.h:79
@ DEVELOP_BLEND_ADD
Definition blend.h:71
@ DEVELOP_BLEND_VIVIDLIGHT
Definition blend.h:78
@ DEVELOP_BLEND_GEOMETRIC_MEAN
Definition blend.h:105
@ DEVELOP_BLEND_SOFTLIGHT
Definition blend.h:76
@ DEVELOP_BLEND_COLORADJUST
Definition blend.h:87
@ DEVELOP_BLEND_DARKEN
Definition blend.h:68
@ DEVELOP_BLEND_LAB_B
Definition blend.h:97
@ DEVELOP_BLEND_LAB_LIGHTNESS
Definition blend.h:91
@ DEVELOP_BLEND_DIFFERENCE2
Definition blend.h:88
@ DEVELOP_BLEND_SUBTRACT_INVERSE
Definition blend.h:102
@ DEVELOP_BLEND_RGB_G
Definition blend.h:99
@ DEVELOP_MASK_RASTER
Definition blend.h:118
@ DEVELOP_MASK_PARAMETRIC
Definition blend.h:117
@ DEVELOP_MASK_ENABLED
Definition blend.h:115
@ DEVELOP_MASK_SHAPE
Definition blend.h:116
static int _blendop_blendif_disp_alternative_worker(GtkWidget *widget, dt_iop_module_t *module, int mode, float(*scale_callback)(GtkWidget *, float, int), const char *label)
Definition blend_gui.c:770
static void _blendop_blendif_update_tab(dt_iop_module_t *module, const int tab)
Definition blend_gui.c:940
static void _blendop_masks_group_selection_changed(GtkTreeSelection *selection, dt_iop_module_t *module)
Definition blend_gui.c:2179
const dt_iop_gui_blendif_colorstop_t _gradient_HSL_hue[]
Definition blend_gui.c:254
const dt_iop_gui_blendif_colorstop_t _gradient_JzCzhz_hue[]
Definition blend_gui.c:267
static gboolean _blendop_masks_is_group_with_shapes(const dt_masks_form_t *mask_form)
Definition blend_gui.c:1594
static const GdkPixbuf * _blendop_masks_get_inverse_icon(const dt_iop_gui_blend_data_t *bd, const int state)
Definition blend_gui.c:1564
static void _blendop_masks_mode_changed(GtkToggleButton *togglebutton, dt_iop_module_t *module)
Definition blend_gui.c:1240
const dt_iop_gui_blendif_colorstop_t _gradient_blue[]
Definition blend_gui.c:218
void dt_iop_gui_init_masks(GtkBox *blendw, dt_iop_module_t *module)
Definition blend_gui.c:3819
static void _blendop_masks_all_selection_changed(GtkTreeSelection *selection, dt_iop_module_t *module)
Definition blend_gui.c:1910
static void _blendif_scale_print_default(float value, float boost_factor, char *string, int n)
Definition blend_gui.c:475
void dt_iop_gui_init_blending(dt_iop_module_t *module)
Definition blend_gui.c:5023
static void _blendif_hide_output_channels(GtkMenuItem *menuitem, dt_iop_module_t *module)
Definition blend_gui.c:2958
static gboolean _blendop_masks_confirm_delete(const char *form_name)
Definition blend_gui.c:2425
static GtkWidget * _blendop_masks_group_ctx_menu(dt_iop_gui_blend_data_t *bd, dt_iop_module_t *module, const int formid, const int parentid, const int state, const int index, const int list_length)
Definition blend_gui.c:2324
static int _blendif_print_digits_picker(float value)
Definition blend_gui.c:860
static gboolean _blendop_masks_group_handle_action_click(GtkWidget *treeview, GtkTreePath *path, GtkTreeViewColumn *column, dt_iop_module_t *module)
Definition blend_gui.c:2468
#define BLEND_MASKMODE_CONF_KEY
Definition blend_gui.c:79
void dt_iop_gui_init_raster(GtkBox *blendw, dt_iop_module_t *module)
Definition blend_gui.c:4151
static void _blendif_cook(dt_iop_colorspace_type_t cst, const float *in, float *out, const dt_iop_order_iccprofile_info_t *const work_profile)
Definition blend_gui.c:399
static GtkWidget * _blendop_create_enable_toggle(dt_iop_module_t *module, const unsigned int mask_bit)
Definition blend_gui.c:4247
static void _blendif_select_colorspace(GtkMenuItem *menuitem, dt_iop_module_t *module)
Definition blend_gui.c:2938
static void _blendop_masks_group_name_commit(dt_iop_module_t *module, const gchar *new_text)
Definition blend_gui.c:1848
static void _blendop_masks_all_rename_callback(GtkWidget *menu_item, dt_iop_module_t *module)
Definition blend_gui.c:2044
static gboolean _blendif_clean_output_channels(dt_iop_module_t *module)
Definition blend_gui.c:494
static gboolean _blendop_blendif_invert(GtkButton *button, GdkEventButton *event, dt_iop_module_t *module)
Definition blend_gui.c:1313
const dt_develop_name_value_t dt_develop_invert_mask_names[]
Definition blend_gui.c:160
const dt_develop_name_value_t dt_develop_mask_mode_names[]
Definition blend_gui.c:137
static void _blendop_masks_handler_callback(gpointer instance, const int formid, const int parentid, const dt_masks_event_t event, dt_iop_module_t *module)
Definition blend_gui.c:2636
static dt_masks_form_group_t * _blendop_masks_find_group_entry(dt_masks_form_t *group_form, const int formid, int *index)
Definition blend_gui.c:1509
static int _blendop_masks_group_tree_append(const dt_iop_gui_blend_data_t *bd, GtkTreeStore *tree_store, GtkTreeIter *parent_iter, const dt_masks_form_t *parent_group)
Definition blend_gui.c:1649
static void _blendif_options_callback(GtkButton *button, GdkEventButton *event, dt_iop_module_t *module)
Definition blend_gui.c:2973
static void _blendif_show_output_channels(GtkMenuItem *menuitem, dt_iop_module_t *module)
Definition blend_gui.c:2947
_blendop_masks_all_cols_t
Definition blend_gui.c:1436
@ BLENDOP_MASKS_ALL_COL_STATUS_MARKUP
Definition blend_gui.c:1442
@ BLENDOP_MASKS_ALL_COL_MARKUP
Definition blend_gui.c:1441
@ BLENDOP_MASKS_ALL_COL_SENSITIVE
Definition blend_gui.c:1440
@ BLENDOP_MASKS_ALL_COL_NAME
Definition blend_gui.c:1438
@ BLENDOP_MASKS_ALL_COL_COUNT
Definition blend_gui.c:1443
@ BLENDOP_MASKS_ALL_COL_FORMID
Definition blend_gui.c:1439
@ BLENDOP_MASKS_ALL_COL_ACTIVE
Definition blend_gui.c:1437
static gboolean _blendop_masks_group_move_by_index(dt_masks_form_t *group_form, const int index, const gboolean move_up)
Definition blend_gui.c:2241
void dt_iop_gui_init_blendif(GtkBox *blendw, dt_iop_module_t *module, GtkWidget *blendif_header)
Definition blend_gui.c:3573
static void _blendop_sync_toggle_state(GtkWidget *toggle, const gboolean available, const gboolean enabled, GtkWidget *content)
Definition blend_gui.c:4326
static void _blendop_blendif_channel_display_toggled(GtkToggleButton *button, dt_iop_module_t *module)
Persistently display the current parametric input or output channel.
Definition blend_gui.c:3202
static gboolean _blendop_blendif_enter(GtkWidget *widget, GdkEventCrossing *event, dt_iop_module_t *module)
Definition blend_gui.c:3250
static void _blendop_masks_mode_callback(const unsigned int mask_mode, dt_iop_gui_blend_data_t *data)
Definition blend_gui.c:535
static GtkWidget * _combobox_new_from_list(dt_iop_module_t *module, const gchar *label, const dt_develop_name_value_t *list, uint32_t *field, const gchar *tooltip)
Definition blend_gui.c:4221
static gboolean _blendop_masks_group_button_pressed(GtkWidget *treeview, GdkEventButton *event, dt_iop_module_t *module)
Definition blend_gui.c:2494
static GtkWidget * _blendop_create_toggle_page(GtkWidget *notebook, const gchar *label, gchar *help_url, dt_iop_module_t *module, const unsigned int mask_bit, GtkWidget **toggle, GtkWidget **content)
Definition blend_gui.c:4292
void dt_iop_gui_init_blending_body(GtkWidget *container, dt_iop_module_t *module)
Definition blend_gui.c:4787
static void _blendop_masks_init_icons(dt_iop_gui_blend_data_t *bd)
Definition blend_gui.c:1530
static GtkWidget * _blendop_create_notebook_page(GtkWidget *notebook, const gchar *label, gchar *help_url, GtkWidget **content)
Definition blend_gui.c:4281
static void _blendop_blendif_details_callback(GtkWidget *slider, dt_iop_gui_blend_data_t *data)
Definition blend_gui.c:1162
static int _blendop_masks_group_tree_append_entry(const dt_iop_gui_blend_data_t *bd, GtkTreeStore *tree_store, GtkTreeIter *parent_iter, const dt_masks_form_group_t *group_entry, const dt_masks_form_t *mask_form, const int index)
Definition blend_gui.c:1614
static void _blendif_scale(dt_iop_gui_blend_data_t *data, dt_iop_colorspace_type_t cst, const float *in, float *out, const dt_iop_order_iccprofile_info_t *work_profile, int in_out)
Definition blend_gui.c:353
static void _blendop_blendif_channel_mask_view_toggle(GtkWidget *widget, dt_iop_module_t *module, dt_dev_pixelpipe_display_mask_t mode)
Definition blend_gui.c:3105
static void _blendop_masks_combine_callback(GtkWidget *combo, dt_iop_gui_blend_data_t *data)
Definition blend_gui.c:585
static gboolean _blendop_masks_show_and_edit(GtkWidget *widget, GdkEventButton *event, dt_iop_module_t *self)
Definition blend_gui.c:1361
const dt_iop_gui_blendif_colorstop_t _gradient_green[]
Definition blend_gui.c:210
static void _raster_polarity_callback(GtkToggleButton *togglebutton, dt_iop_module_t *self)
Definition blend_gui.c:4138
const char * slider_tooltip[]
Definition blend_gui.c:3477
static void _blendop_blendif_log_scale_toggled(GtkToggleButton *button, dt_iop_module_t *module)
Toggle the alternative scale of one parametric-mask slider.
Definition blend_gui.c:811
static void _blendop_blendif_sync_channel_display_buttons(dt_iop_module_t *module)
Synchronize input/output channel toggles from the active preview request.
Definition blend_gui.c:3174
static gboolean _blendop_blendif_leave_delayed(gpointer data)
Definition blend_gui.c:3317
#define COLORSTOPS(gradient)
Definition blend_gui.c:3401
static gboolean _blendop_blendif_leave(GtkWidget *widget, GdkEventCrossing *event, dt_iop_module_t *module)
Definition blend_gui.c:3363
const dt_iop_gui_blendif_channel_t rgb_channels[]
Definition blend_gui.c:3424
static gboolean _blendop_masks_all_button_pressed(GtkWidget *treeview, GdkEventButton *event, dt_iop_module_t *module)
Definition blend_gui.c:2100
static gboolean _blendop_masks_group_query_tooltip(GtkWidget *treeview, gint x, gint y, gboolean keyboard_tip, GtkTooltip *tooltip, dt_iop_module_t *module)
Definition blend_gui.c:2552
static void _blendop_compositing_changed(GtkWidget *combobox, dt_iop_gui_blend_data_t *data)
Definition blend_gui.c:569
static gboolean _blendif_are_output_channels_used(const dt_develop_blend_params_t *const blend, const dt_develop_blend_colorspace_t cst)
Definition blend_gui.c:481
static void _blendop_masks_group_name_activate(GtkEntry *entry, dt_iop_module_t *module)
Definition blend_gui.c:1867
_channel_indexes
Definition blend_gui.c:281
@ CHANNEL_INDEX_l
Definition blend_gui.c:293
@ CHANNEL_INDEX_hz
Definition blend_gui.c:296
@ CHANNEL_INDEX_Cz
Definition blend_gui.c:295
@ CHANNEL_INDEX_b
Definition blend_gui.c:284
@ CHANNEL_INDEX_g
Definition blend_gui.c:287
@ CHANNEL_INDEX_H
Definition blend_gui.c:291
@ CHANNEL_INDEX_Jz
Definition blend_gui.c:294
@ CHANNEL_INDEX_S
Definition blend_gui.c:292
@ CHANNEL_INDEX_L
Definition blend_gui.c:282
@ CHANNEL_INDEX_C
Definition blend_gui.c:285
@ CHANNEL_INDEX_a
Definition blend_gui.c:283
@ CHANNEL_INDEX_G
Definition blend_gui.c:289
@ CHANNEL_INDEX_B
Definition blend_gui.c:290
@ CHANNEL_INDEX_h
Definition blend_gui.c:286
@ CHANNEL_INDEX_R
Definition blend_gui.c:288
const dt_iop_gui_blendif_colorstop_t _gradient_b[]
Definition blend_gui.c:185
static gboolean _blendop_masks_find_iter_by_formid(GtkTreeModel *model, GtkTreeIter *iter, const int formid_col, const int formid)
Definition blend_gui.c:1692
static void _blendop_masks_group_operation_callback(GtkWidget *menu_item, gpointer user_data)
Definition blend_gui.c:2158
static gboolean _blendop_blendif_key_press(GtkWidget *widget, GdkEventKey *event, dt_iop_module_t *module)
Definition blend_gui.c:3380
static void _blendop_blendif_sliders_reset_callback(GtkDarktableGradientSlider *slider, dt_iop_gui_blend_data_t *data)
Definition blend_gui.c:666
static void _blendop_masks_invert_toggled(GtkToggleButton *togglebutton, dt_iop_gui_blend_data_t *data)
Definition blend_gui.c:611
_blendop_masks_group_cols_t
Definition blend_gui.c:1447
@ BLENDOP_MASKS_GROUP_COL_INDEX
Definition blend_gui.c:1454
@ BLENDOP_MASKS_GROUP_COL_OP_ICON
Definition blend_gui.c:1448
@ BLENDOP_MASKS_GROUP_COL_STATE
Definition blend_gui.c:1453
@ BLENDOP_MASKS_GROUP_COL_COUNT
Definition blend_gui.c:1455
@ BLENDOP_MASKS_GROUP_COL_FORMID
Definition blend_gui.c:1451
@ BLENDOP_MASKS_GROUP_COL_NAME
Definition blend_gui.c:1450
@ BLENDOP_MASKS_GROUP_COL_INV_ICON
Definition blend_gui.c:1449
@ BLENDOP_MASKS_GROUP_COL_PARENTID
Definition blend_gui.c:1452
static void _raster_value_changed_callback(GtkWidget *widget, struct dt_iop_module_t *module)
Definition blend_gui.c:4083
static void _blendop_masks_all_toggled(GtkCellRendererToggle *cell, gchar *path_string, dt_iop_module_t *module)
Definition blend_gui.c:1940
static void _blendop_blendif_polarity_callback(GtkToggleButton *togglebutton, dt_iop_gui_blend_data_t *data)
Definition blend_gui.c:689
static void _blendif_scale_print_hue(float value, float boost_factor, char *string, int n)
Definition blend_gui.c:470
static gboolean _blendop_masks_shape_can_start(GtkWidget *button, dt_iop_module_t *module, dt_masks_type_t type, gpointer user_data)
Definition blend_gui.c:1346
static gboolean _blendif_blend_parameter_enabled(dt_develop_blend_colorspace_t csp, dt_develop_blend_mode_t mode)
Definition blend_gui.c:325
static gboolean _blendop_masks_polarity_callback(GtkToggleButton *togglebutton, GdkEventButton *event, dt_iop_module_t *self)
Definition blend_gui.c:1414
static gboolean _blendop_masks_all_handle_left_click(GtkWidget *treeview, GtkTreePath *path, GtkTreeViewColumn *column, dt_iop_module_t *module)
Definition blend_gui.c:2066
static void _blendop_blendif_tab_switch(GtkNotebook *notebook, GtkWidget *page, guint page_num, dt_iop_gui_blend_data_t *data)
Definition blend_gui.c:1086
static GtkWidget * _blendop_masks_create_shape_buttons(dt_iop_module_t *module, dt_iop_gui_blend_data_t *bd)
Definition blend_gui.c:2300
const dt_iop_gui_blendif_channel_t Lab_channels[]
Definition blend_gui.c:3404
static float magnifier_scale_callback(GtkWidget *self, float inval, int dir)
Definition blend_gui.c:743
static void _blendop_masks_all_name_edited(GtkCellRendererText *cell, gchar *path_string, gchar *new_text, dt_iop_module_t *module)
Definition blend_gui.c:1882
void dt_iop_gui_update_blending(dt_iop_module_t *module)
Definition blend_gui.c:4336
const dt_iop_gui_blendif_colorstop_t _gradient_chroma[]
Definition blend_gui.c:227
static void _blendop_blend_mode_callback(GtkWidget *combo, dt_iop_gui_blend_data_t *data)
Definition blend_gui.c:545
static float log10_scale_callback(GtkWidget *self, float inval, int dir)
Definition blend_gui.c:722
const dt_develop_name_value_t dt_develop_blend_colorspace_names[]
Definition blend_gui.c:129
static dt_iop_colorspace_type_t _blendop_blendif_get_picker_colorspace(dt_iop_gui_blend_data_t *bd)
Definition blend_gui.c:831
static int _blendif_print_digits_default(float value)
Definition blend_gui.c:444
static void _blendop_masks_apply_and_commit(dt_iop_module_t *module)
Definition blend_gui.c:1837
static int _blendif_print_digits_ab(float value)
Definition blend_gui.c:455
static void _update_gradient_slider_pickers(GtkWidget *callback_dummy, dt_iop_module_t *module)
Definition blend_gui.c:869
const dt_iop_gui_blendif_channel_t rgbj_channels[]
Definition blend_gui.c:3451
static gboolean _add_blendmode_combo(GtkWidget *combobox, dt_develop_blend_mode_t mode)
Definition blend_gui.c:4206
void dt_iop_gui_blending_lose_focus(dt_iop_module_t *module)
Definition blend_gui.c:4624
static void _blendop_masks_check_id(dt_masks_form_t *mask_form)
Definition blend_gui.c:1465
static void _blendop_masks_all_duplicate_callback(GtkWidget *menu_item, dt_iop_module_t *module)
Definition blend_gui.c:2033
static gboolean _blendif_change_blend_colorspace(dt_iop_module_t *module, dt_develop_blend_colorspace_t cst)
Definition blend_gui.c:2885
const dt_iop_gui_blendif_colorstop_t _gradient_L[]
Definition blend_gui.c:165
const dt_iop_gui_blendif_colorstop_t _gradient_LCh_hue[]
Definition blend_gui.c:238
static dt_masks_form_t * _blendop_masks_group_create(dt_iop_module_t *module)
Definition blend_gui.c:1485
static int _blendop_blendif_disp_alternative_log(GtkWidget *widget, dt_iop_module_t *module, int mode)
Definition blend_gui.c:794
static int _blendop_blendif_disp_alternative_mag(GtkWidget *widget, dt_iop_module_t *module, int mode)
Definition blend_gui.c:789
static gboolean _blendop_masks_is_single_group_wrapper(const dt_masks_form_t *mask_form)
Definition blend_gui.c:1574
void dt_masks_iop_update(dt_iop_module_t *module)
Definition blend_gui.c:3743
static float _get_boost_factor(const dt_iop_gui_blend_data_t *data, const int channel, const int in_out)
Definition blend_gui.c:348
const dt_develop_name_value_t dt_develop_blend_mode_flag_names[]
Definition blend_gui.c:124
static void _blendop_masks_all_delete_callback(GtkWidget *menu_item, dt_iop_module_t *module)
Definition blend_gui.c:2019
static const GdkPixbuf * _blendop_masks_get_op_icon(const dt_iop_gui_blend_data_t *bd, const int state, const int index)
Definition blend_gui.c:1552
static gboolean _blendop_blendif_showmask_clicked(GtkToggleButton *button, GdkEventButton *event, dt_iop_module_t *module)
Definition blend_gui.c:1177
static void _blendop_masks_edit_list_toggle(GtkToggleButton *togglebutton, dt_iop_module_t *module)
Definition blend_gui.c:2277
static void _blendop_masks_group_update_row(dt_iop_module_t *module, const int formid, const int parentid)
Definition blend_gui.c:2607
static void _blendop_blendif_channel_mask_view(GtkWidget *widget, dt_iop_module_t *module, dt_dev_pixelpipe_display_mask_t mode)
Definition blend_gui.c:3057
static void _raster_combo_populate(GtkWidget *w, void *m)
Definition blend_gui.c:4045
void dt_iop_gui_cleanup_blending_body(dt_iop_module_t *module)
Definition blend_gui.c:4693
const dt_develop_name_value_t dt_develop_combine_masks_names[]
Definition blend_gui.c:146
const dt_develop_name_value_t dt_develop_blend_mode_names[]
Definition blend_gui.c:82
static void _blendop_blendif_disp_alternative_reset(GtkWidget *widget, dt_iop_module_t *module)
Definition blend_gui.c:799
static void _blendop_update_top_enable_label(dt_iop_module_t *module)
Definition blend_gui.c:4258
void dt_iop_gui_update_raster(dt_iop_module_t *module)
Definition blend_gui.c:4126
static void _blendop_masks_group_delete(dt_iop_module_t *module, const int formid, const int parentid)
Definition blend_gui.c:2446
static void _blendop_masks_refresh_lists(dt_iop_module_t *module)
Definition blend_gui.c:1719
static void _notebook_append_full_width_page(GtkWidget *notebook, GtkWidget *page, const gchar *label)
Definition blend_gui.c:4240
void dt_iop_gui_cleanup_blending(dt_iop_module_t *module)
Definition blend_gui.c:4185
const dt_iop_gui_blendif_colorstop_t _gradient_gray[]
Definition blend_gui.c:196
static gboolean _blendop_masks_group_name_focus_out(GtkWidget *widget, GdkEventFocus *event, dt_iop_module_t *module)
Definition blend_gui.c:1873
void dt_iop_gui_blending_reload_defaults(dt_iop_module_t *module)
Definition blend_gui.c:4685
static dt_iop_colorspace_type_t _blendif_colorpicker_cst(dt_iop_gui_blend_data_t *data)
Definition blend_gui.c:302
const dt_develop_name_value_t dt_develop_feathering_guide_names[]
Definition blend_gui.c:153
const dt_iop_gui_blendif_colorstop_t _gradient_red[]
Definition blend_gui.c:202
void dt_iop_gui_init_contours(GtkBox *blendw, dt_iop_module_t *module)
Definition blend_gui.c:3731
const dt_iop_gui_blendif_colorstop_t _gradient_a[]
Definition blend_gui.c:173
static void _blendop_blendif_boost_factor_callback(GtkWidget *slider, dt_iop_gui_blend_data_t *data)
Definition blend_gui.c:1122
static dt_masks_form_t * _blendop_masks_group_from_module(dt_iop_module_t *module)
Definition blend_gui.c:1458
static void _blendop_blending_notebook_switch(GtkNotebook *notebook, GtkWidget *page, guint page_num, dt_iop_gui_blend_data_t *data)
Persist the active blending notebook tab in anselrc.
Definition blend_gui.c:1112
static gboolean _blendop_masks_group_find_row(GtkTreeModel *model, GtkTreeIter *parent, const int formid, const int parentid, GtkTreeIter *out)
Definition blend_gui.c:2581
static gboolean _blendop_blendif_reset(GtkButton *button, GdkEventButton *event, dt_iop_module_t *module)
Definition blend_gui.c:1298
static void _blendop_blendif_sliders_callback(GtkDarktableGradientSlider *slider, dt_iop_gui_blend_data_t *data)
Definition blend_gui.c:624
static void _blendif_scale_print_ab(float value, float boost_factor, char *string, int n)
Definition blend_gui.c:464
static void _blendop_masks_group_move_callback(GtkWidget *menu_item, dt_iop_module_t *module)
Definition blend_gui.c:2262
#define NEUTRAL_GRAY
Definition blend_gui.c:78
static void _blendop_toggle_button_set_active(GtkWidget *toggle, const gboolean enabled)
Definition blend_gui.c:4314
static void _blendop_masks_group_unlink(dt_iop_module_t *module, const int formid, const int parentid)
Definition blend_gui.c:2406
void dt_iop_gui_update_blendif(dt_iop_module_t *module)
Definition blend_gui.c:3484
void dtgtk_button_set_active(GtkDarktableButton *button, gboolean active)
Definition button.c:176
GtkWidget * dtgtk_button_new(DTGTKCairoPaintIconFunc paint, gint paintflags, void *paintdata)
Definition button.c:134
#define DTGTK_BUTTON(obj)
Definition button.h:39
dt_iop_colorspace_type_t
@ IOP_CS_RAW
@ IOP_CS_LCH
@ IOP_CS_JZCZHZ
@ IOP_CS_RGB
@ IOP_CS_HSL
@ IOP_CS_LAB
@ IOP_CS_NONE
void dt_iop_color_picker_reset(dt_iop_module_t *module, gboolean keep)
void dt_iop_color_picker_set_cst(dt_iop_module_t *module, const dt_iop_colorspace_type_t picker_cst)
GtkWidget * dt_color_picker_new(dt_iop_module_t *module, dt_iop_color_picker_kind_t kind, GtkWidget *w)
dt_iop_colorspace_type_t dt_iop_color_picker_get_active_cst(dt_iop_module_t *module)
@ DT_COLOR_PICKER_AREA
@ DT_COLOR_PICKER_POINT_AREA
@ DT_LIB_COLORPICKER_SIZE_BOX
Definition colorpicker.h:38
const dt_colormatrix_t dt_aligned_pixel_t out
typedef void((*dt_cache_allocate_t)(void *userdata, dt_cache_entry_t *entry))
char * key
int type
void dt_conf_set_int(const char *name, int val)
int dt_conf_get_int(const char *name)
void dt_control_log(const char *msg,...)
Definition control.c:761
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
void dt_control_queue_redraw_widget(GtkWidget *widget)
threadsafe request of redraw of specific widget. Use this function if you need to redraw a specific w...
Definition control.c:906
void dt_control_hinter_message(const struct dt_control_t *s, const char *message)
Definition control.c:918
uint32_t view(const dt_view_t *self)
Definition darkroom.c:227
darktable_t darktable
Definition darktable.c:181
#define DT_ALIGNED_PIXEL
Definition darktable.h:389
#define dt_free(ptr)
Definition darktable.h:456
static gchar * delete_underscore(const char *s)
Definition darktable.h:1083
static const dt_aligned_pixel_simd_t value
Definition darktable.h:577
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_dev_add_history_item(dev, module, enable, redraw)
#define dt_dev_pixelpipe_update_history_main(dev)
#define dt_dev_pixelpipe_update_zoom_main(dev)
#define dt_dev_pixelpipe_update_history_all(dev)
void dt_dev_masks_selection_change(dt_develop_t *dev, struct dt_iop_module_t *module, const int selectid, const int throw_event)
Definition develop.c:1202
gchar * dt_dev_get_masks_group_name(const struct dt_iop_module_t *module)
Definition develop.c:1479
gchar * dt_history_item_get_name(const struct dt_iop_module_t *module)
Definition develop.c:1493
dt_dev_pixelpipe_display_mask_t
Definition develop.h:116
@ DT_DEV_PIXELPIPE_DISPLAY_a
Definition develop.h:122
@ DT_DEV_PIXELPIPE_DISPLAY_OUTPUT
Definition develop.h:120
@ DT_DEV_PIXELPIPE_DISPLAY_G
Definition develop.h:125
@ DT_DEV_PIXELPIPE_DISPLAY_L
Definition develop.h:121
@ DT_DEV_PIXELPIPE_DISPLAY_CHANNEL
Definition develop.h:119
@ DT_DEV_PIXELPIPE_DISPLAY_LCH_h
Definition develop.h:129
@ DT_DEV_PIXELPIPE_DISPLAY_JzCzhz_hz
Definition develop.h:135
@ DT_DEV_PIXELPIPE_DISPLAY_HSL_H
Definition develop.h:130
@ DT_DEV_PIXELPIPE_DISPLAY_HSL_S
Definition develop.h:131
@ DT_DEV_PIXELPIPE_DISPLAY_JzCzhz_Cz
Definition develop.h:134
@ DT_DEV_PIXELPIPE_DISPLAY_LCH_C
Definition develop.h:128
@ DT_DEV_PIXELPIPE_DISPLAY_b
Definition develop.h:123
@ DT_DEV_PIXELPIPE_DISPLAY_STICKY
Definition develop.h:139
@ DT_DEV_PIXELPIPE_DISPLAY_MASK
Definition develop.h:118
@ DT_DEV_PIXELPIPE_DISPLAY_JzCzhz_Jz
Definition develop.h:133
@ DT_DEV_PIXELPIPE_DISPLAY_NONE
Definition develop.h:117
@ DT_DEV_PIXELPIPE_DISPLAY_HSL_l
Definition develop.h:132
@ DT_DEV_PIXELPIPE_DISPLAY_GRAY
Definition develop.h:127
@ DT_DEV_PIXELPIPE_DISPLAY_B
Definition develop.h:126
@ DT_DEV_PIXELPIPE_DISPLAY_R
Definition develop.h:124
static GdkPixbuf * dt_draw_get_pixbuf_from_cairo(DTGTKCairoPaintIconFunc paint, const int width, const int height)
Definition draw.h:947
void dtgtk_cairo_paint_masks_inverse(cairo_t *cr, gint x, gint y, gint w, gint h, gint flags, void *data)
void dtgtk_cairo_paint_masks_difference(cairo_t *cr, gint x, gint y, gint w, gint h, gint flags, void *data)
void dtgtk_cairo_paint_presets(cairo_t *cr, gint x, gint y, gint w, gint h, gint flags, void *data)
void dtgtk_cairo_paint_showmask(cairo_t *cr, gint x, gint y, gint w, gint h, gint flags, void *data)
void dtgtk_cairo_paint_masks_union(cairo_t *cr, gint x, gint y, gint w, gint h, gint flags, void *data)
void dtgtk_cairo_paint_colorpicker(cairo_t *cr, gint x, gint y, gint w, gint h, gint flags, void *data)
void dtgtk_cairo_paint_masks_exclusion(cairo_t *cr, gint x, gint y, gint w, gint h, gint flags, void *data)
void dtgtk_cairo_paint_eye_toggle(cairo_t *cr, gint x, gint y, gint w, gint h, gint flags, void *data)
void dtgtk_cairo_paint_reset(cairo_t *cr, gint x, gint y, gint w, gint h, gint flags, void *data)
void dtgtk_cairo_paint_masks_intersection(cairo_t *cr, gint x, gint y, gint w, gint h, gint flags, void *data)
void dtgtk_cairo_paint_invert(cairo_t *cr, gint x, gint y, gint w, gint h, gint flags, void *data)
void dtgtk_cairo_paint_masks_edit(cairo_t *cr, gint x, gint y, gint w, gint h, gint flags, void *data)
@ CPF_ALTER
Definition dtgtk/paint.h:70
static int dt_pthread_mutex_unlock(dt_pthread_mutex_t *mutex) RELEASE(mutex) NO_THREAD_SAFETY_ANALYSIS
Definition dtpthread.h:374
static int dt_pthread_mutex_init(dt_pthread_mutex_t *mutex, const pthread_mutexattr_t *mutexattr)
Definition dtpthread.h:359
static int dt_pthread_mutex_destroy(dt_pthread_mutex_t *mutex)
Definition dtpthread.h:379
static int dt_pthread_mutex_lock(dt_pthread_mutex_t *mutex) ACQUIRE(mutex) NO_THREAD_SAFETY_ANALYSIS
Definition dtpthread.h:364
void dtgtk_gradient_slider_multivalue_set_resetvalue(GtkDarktableGradientSlider *gslider, gdouble value, gint pos)
void dtgtk_gradient_slider_multivalue_set_value(GtkDarktableGradientSlider *gslider, gdouble value, gint pos)
void dtgtk_gradient_slider_multivalue_set_increment(GtkDarktableGradientSlider *gslider, gdouble value)
void dtgtk_gradient_slider_multivalue_set_picker(GtkDarktableGradientSlider *gslider, gdouble value)
gdouble dtgtk_gradient_slider_multivalue_get_value(GtkDarktableGradientSlider *gslider, gint pos)
void dtgtk_gradient_slider_multivalue_set_picker_meanminmax(GtkDarktableGradientSlider *gslider, gdouble mean, gdouble min, gdouble max)
void dtgtk_gradient_slider_multivalue_clear_stops(GtkDarktableGradientSlider *gslider)
void dtgtk_gradient_slider_multivalue_set_scale_callback(GtkDarktableGradientSlider *gslider, float(*callback)(GtkWidget *self, float value, int dir))
void dtgtk_gradient_slider_multivalue_set_stop(GtkDarktableGradientSlider *gslider, gfloat position, GdkRGBA color)
void dtgtk_gradient_slider_multivalue_set_marker(GtkDarktableGradientSlider *gslider, gint mark, gint pos)
GtkWidget * dtgtk_gradient_slider_multivalue_new_with_name(gint positions, gchar *name)
#define DTGTK_GRADIENT_SLIDER_MULTIVALUE(obj)
@ GRADIENT_SLIDER_GET
@ GRADIENT_SLIDER_SET
@ GRADIENT_SLIDER_MARKER_UPPER_FILLED_BIG
@ GRADIENT_SLIDER_MARKER_LOWER_OPEN_BIG
@ GRADIENT_SLIDER_MARKER_UPPER_OPEN_BIG
@ GRADIENT_SLIDER_MARKER_LOWER_FILLED_BIG
void dt_gui_menu_popup(GtkMenu *menu, GtkWidget *button, GdkGravity widget_anchor, GdkGravity menu_anchor)
Definition gtk.c:2953
void dt_gui_new_collapsible_section(dt_gui_collapsible_section_t *cs, const char *confname, const char *label, GtkBox *parent, GtkPackType pack)
Create a collapsible section and pack it into the parent box.
Definition gtk.c:3102
GtkWidget * dt_ui_notebook_page(GtkNotebook *notebook, const char *text, const char *tooltip)
Definition gtk.c:2259
void dt_gui_update_collapsible_section(dt_gui_collapsible_section_t *cs)
Definition gtk.c:3080
GdkModifierType dt_key_modifier_state()
Definition gtk.c:2186
void dt_gui_container_destroy_children(GtkContainer *container)
Definition gtk.c:2919
void dt_gui_add_help_link(GtkWidget *widget, char *link)
Definition gtk.c:2022
GtkWidget * dt_ui_scroll_wrap(GtkWidget *w, gint min_size, char *config_str, dt_ui_resize_mode_t mode)
Wrap a scrollable content widget in a recessed, vertically resizable scrolled window.
Definition gtk.c:2713
void dt_gui_add_class(GtkWidget *widget, const gchar *class_name)
Definition gtk.c:133
GtkWidget * dt_ui_main_window(dt_ui_t *ui)
get the main window widget
@ DT_UI_RESIZE_DYNAMIC
Definition gtk.h:260
#define DT_GUI_BOX_SPACING
Definition gtk.h:109
#define DT_PIXEL_APPLY_DPI(value)
Definition gtk.h:90
static GtkWidget * dt_ui_label_new(const gchar *str)
Definition gtk.h:461
#define DT_GUI_MODULE(x)
const char * tooltip
Definition image.h:251
void dt_iop_add_remove_mask_indicator(dt_iop_module_t *module)
Definition imageop.c:2639
void dt_iop_gui_update(dt_iop_module_t *module)
Definition imageop.c:2091
void dt_iop_gui_update_header(dt_iop_module_t *module)
Definition imageop.c:1177
void dt_iop_request_focus(dt_iop_module_t *module)
Definition imageop.c:2169
void dt_iop_set_cache_bypass(dt_iop_module_t *module, gboolean state)
Definition imageop.c:2915
void dt_iop_set_mask_mode(dt_iop_module_t *module, int mask_mode)
Definition imageop.c:1616
static gboolean dt_iop_colorspace_is_rgb(const dt_iop_colorspace_type_t cst)
Definition imageop.h:213
@ IOP_FLAGS_SUPPORTS_BLENDING
Definition imageop.h:167
@ IOP_CS_RGB_DISPLAY
Definition imageop.h:210
GtkWidget * dt_iop_togglebutton_new_no_register(dt_iop_module_t *self, const char *section, const gchar *label, const gchar *ctrl_label, GCallback callback, gboolean local, guint accel_key, GdkModifierType mods, DTGTKCairoPaintIconFunc paint, GtkWidget *box)
@ DT_INTROSPECTION_TYPE_ENUM
@ DT_INTROSPECTION_TYPE_FLOAT
const char * model
dt_iop_order_iccprofile_info_t * dt_ioppr_get_pipe_current_profile_info(dt_iop_module_t *module, const struct dt_dev_pixelpipe_t *pipe)
dt_iop_order_iccprofile_info_t * dt_ioppr_get_iop_work_profile_info(struct dt_iop_module_t *module, GList *iop_list)
static const float x
#define DEVELOP_BLENDIF_SIZE
Definition lightroom.c:235
float *const restrict const size_t k
float *const restrict const size_t const size_t ch
void apply_operation(struct dt_masks_form_group_t *pt, const dt_masks_state_t apply_state)
Apply a mask state operation on a group entry.
dt_masks_form_group_t * dt_masks_group_add_form(dt_masks_form_t *grp, dt_masks_form_t *form)
void dt_masks_change_form_gui(dt_masks_form_t *newform)
@ DT_MASKS_EDIT_RESTRICTED
Definition masks.h:203
@ DT_MASKS_EDIT_OFF
Definition masks.h:201
@ DT_MASKS_EDIT_FULL
Definition masks.h:202
dt_masks_state_t
Definition masks.h:166
@ DT_MASKS_STATE_DIFFERENCE
Definition masks.h:173
@ DT_MASKS_STATE_INVERSE
Definition masks.h:170
@ DT_MASKS_STATE_INTERSECTION
Definition masks.h:172
@ DT_MASKS_STATE_EXCLUSION
Definition masks.h:174
@ DT_MASKS_STATE_UNION
Definition masks.h:171
void dt_masks_shape_buttons_deactivate_all(GtkWidget *active_button)
Definition masks_gui.c:86
GtkWidget * dt_masks_shape_buttons_create(const dt_masks_shape_buttons_config_t *config)
Build a synchronized toolbar for creating masks shapes.
Definition masks_gui.c:180
dt_masks_type_t
Definition masks.h:129
@ DT_MASKS_NON_CLONE
Definition masks.h:138
@ DT_MASKS_CLONE
Definition masks.h:134
@ DT_MASKS_NONE
Definition masks.h:130
@ DT_MASKS_GROUP
Definition masks.h:133
int dt_masks_group_index_from_formid(const dt_masks_form_t *group_form, int formid)
int dt_masks_center_view_on_form(struct dt_develop_t *dev, const struct dt_masks_form_t *form)
dt_masks_event_t
Definition masks.h:156
@ DT_MASKS_EVENT_UPDATE
Definition masks.h:160
@ DT_MASKS_EVENT_RESET
Definition masks.h:163
@ DT_MASKS_EVENT_ADD
Definition masks.h:158
@ DT_MASKS_EVENT_DELETE
Definition masks.h:161
@ DT_MASKS_EVENT_REMOVE
Definition masks.h:159
@ DT_MASKS_EVENT_CHANGE
Definition masks.h:162
dt_masks_form_t * dt_masks_create(dt_masks_type_t type)
@ DT_MASKS_SHAPE_BUTTONS_ALL
Definition masks.h:953
@ DT_MASKS_SHAPE_BUTTONS_POLYGON
Definition masks.h:947
dt_masks_form_t * dt_masks_get_from_id(dt_develop_t *dev, int id)
void dt_masks_append_form(dt_develop_t *dev, dt_masks_form_t *form)
void dt_masks_form_delete(struct dt_iop_module_t *module, dt_masks_form_t *grp, dt_masks_form_t *form)
#define DEVELOP_MASKS_NB_SHAPES
Definition masks.h:927
dt_masks_form_t * dt_masks_get_visible_form(const struct dt_develop_t *dev)
void dt_masks_set_edit_mode(struct dt_iop_module_t *module, dt_masks_edit_mode_t value)
dt_masks_form_group_t * dt_masks_form_group_from_parentid(int parentid, int formid)
Return the group entry for a (parent, form) pair.
int dt_masks_form_duplicate(dt_develop_t *dev, int formid)
#define M_LN10
Definition math.h:40
static float clamp_range_f(const float x, const float low, const float high)
Definition math.h:96
GtkWidget * ctx_gtk_check_menu_item_new_with_markup_and_pixbuf(const char *label, GdkPixbuf *icon, GtkWidget *menu, void(*activate_callback)(GtkWidget *widget, gpointer user_data), gpointer user_data, const gboolean checked, const gboolean show_checkbox)
Definition menu.c:283
float dt_aligned_pixel_t[4]
const float factor
Definition pdf.h:90
#define eps
Definition rcd.c:81
#define DT_DEBUG_CONTROL_SIGNAL_DISCONNECT(ctlsig, cb, user_data)
Definition signal.h:368
#define DT_DEBUG_CONTROL_SIGNAL_RAISE(ctlsig, signal,...)
Definition signal.h:347
@ DT_SIGNAL_MASK_CHANGED
Definition signal.h:303
@ DT_SIGNAL_DEVELOP_MASKS_GUI_CHANGED
Definition signal.h:307
#define DT_DEBUG_CONTROL_SIGNAL_CONNECT(ctlsig, signal, cb, user_data)
Definition signal.h:357
struct _GtkWidget GtkWidget
Definition splash.h:29
const float uint32_t state[4]
struct dt_gui_gtk_t * gui
Definition darktable.h:775
struct dt_control_signal_t * signals
Definition darktable.h:774
struct dt_bauhaus_t * bauhaus
Definition darktable.h:778
struct dt_develop_t * develop
Definition darktable.h:770
struct dt_control_t * control
Definition darktable.h:773
dt_lib_colorpicker_size_t size
Definition colorpicker.h:63
dt_boundingbox_t box
Definition colorpicker.h:62
struct dt_develop_blend_params_t * blend_params
Definition dev_history.h:55
uint32_t feathering_guide
Definition blend.h:221
dt_dev_operation_t raster_mask_source
Definition blend.h:235
float blendif_parameters[4 *DEVELOP_BLENDIF_SIZE]
Definition blend.h:233
float blendif_boost_factors[DEVELOP_BLENDIF_SIZE]
Definition blend.h:234
gboolean raster_mask_invert
Definition blend.h:238
GList * iop
Definition develop.h:279
GList * history
Definition develop.h:275
struct dt_masks_form_gui_t * form_gui
Definition develop.h:327
struct dt_dev_pixelpipe_t * pipe
Definition develop.h:247
GList * forms
Definition develop.h:321
int32_t reset
Definition gtk.h:172
GtkMenu * presets_popup_menu
Definition gtk.h:169
dt_ui_t * ui
Definition gtk.h:164
GtkWidget * raster_enable
Definition blend.h:328
GtkWidget * blendif_enable
Definition blend.h:329
GtkWidget * masks_content
Definition blend.h:330
GtkWidget * masks_treeview
Definition blend.h:377
GtkWidget * channel_boost_factor_slider
Definition blend.h:365
GtkWidget * opacity_slider
Definition blend.h:347
GtkWidget * raster_polarity
Definition blend.h:399
GtkWidget * lists_box
Definition blend.h:394
GtkWidget * showmask
Definition blend.h:341
GtkWidget * all_shapes_sw
Definition blend.h:386
GtkWidget * blur_radius_slider
Definition blend.h:350
GdkPixbuf * masks_ic_intersection
Definition blend.h:390
GtkWidget * feathering_radius_slider
Definition blend.h:349
GtkWidget * details_slider
Definition blend.h:366
dt_iop_gui_blendif_filter_t filter[2]
Definition blend.h:340
GtkWidget * all_shapes_buttons
Definition blend.h:393
GtkWidget * colorpicker
Definition blend.h:338
GtkWidget * colorpicker_set_values
Definition blend.h:339
GdkPixbuf * masks_ic_union
Definition blend.h:389
GdkPixbuf * masks_ic_inverse
Definition blend.h:388
GtkWidget * masks_group_treeview
Definition blend.h:378
GtkWidget * masks_combo
Definition blend.h:368
int masks_type[DEVELOP_MASKS_NB_SHAPES]
Definition blend.h:370
GdkPixbuf * masks_ic_difference
Definition blend.h:391
GtkTreeViewColumn * group_shapes_col
Definition blend.h:380
GtkWidget * lists_stack
Definition blend.h:387
dt_boundingbox_t picker_set_values_box
Definition blend.h:402
GtkWidget * contrast_slider
Definition blend.h:351
GtkWidget * blend_modes_blend_order
Definition blend.h:344
GtkWidget * masks_combine_combo
Definition blend.h:342
dt_develop_blend_colorspace_t channel_tabs_csp
Definition blend.h:355
GtkWidget * masks_edit
Definition blend.h:371
gboolean picker_set_values_manual_boost_lock
Definition blend.h:403
GtkWidget * blend_mode_parameter_slider
Definition blend.h:345
GtkWidget * group_shapes_label
Definition blend.h:372
GtkWidget * masks_feathering_guide_combo
Definition blend.h:348
const dt_iop_gui_blendif_channel_t * channel
Definition blend.h:357
dt_pthread_mutex_t lock
Definition blend.h:406
GtkWidget * brightness_slider
Definition blend.h:352
GtkWidget * masks_shapes[DEVELOP_MASKS_NB_SHAPES]
Definition blend.h:369
GtkWidget * raster_content
Definition blend.h:331
GtkWidget * blend_modes_combo
Definition blend.h:343
GtkWidget * contours_content
Definition blend.h:333
dt_develop_blend_colorspace_t csp
Definition blend.h:321
dt_iop_module_t *GtkWidget * blending_box
Definition blend.h:324
GtkTreeViewColumn * group_delete_col
Definition blend.h:382
GtkWidget * group_shapes_sw
Definition blend.h:384
dt_gui_collapsible_section_t masks_cs
Definition blend.h:395
GtkWidget * blending_notebook
Definition blend.h:325
GtkWidget * blendif_content
Definition blend.h:332
GtkWidget * masks_invert_combo
Definition blend.h:346
gboolean output_channels_shown
Definition blend.h:363
GdkPixbuf * masks_ic_exclusion
Definition blend.h:392
GtkWidget * raster_combo
Definition blend.h:398
GtkListStore * all_shapes_store
Definition blend.h:383
GtkWidget * masks_polarity
Definition blend.h:373
GtkTreeViewColumn * all_shapes_col
Definition blend.h:385
GtkWidget * top_enable
Definition blend.h:326
GtkWidget * masks_enable
Definition blend.h:327
GtkTreeStore * group_shapes_store
Definition blend.h:379
dt_dev_pixelpipe_display_mask_t save_for_leave
Definition blend.h:360
GtkWidget * wire_shape_toggle
Definition blend.h:374
GtkNotebook * channel_tabs
Definition blend.h:362
gboolean picker_set_values_box_valid
Definition blend.h:401
dt_develop_blend_colorspace_t blend_modes_csp
Definition blend.h:354
GtkTreeViewColumn * group_unlink_col
Definition blend.h:381
const dt_iop_gui_blendif_colorstop_t * colorstops
Definition blend.h:276
void(* scale_print)(float value, float boost_factor, char *string, int n)
Definition blend.h:281
int(* altdisplay)(GtkWidget *, dt_iop_module_t *, int)
Definition blend.h:282
dt_develop_blendif_channels_t param_channels[2]
Definition blend.h:279
dt_dev_pixelpipe_display_mask_t display_channel
Definition blend.h:280
GtkWidget * channel_display
Definition blend.h:293
GtkLabel * label[4]
Definition blend.h:290
GtkDarktableGradientSlider * slider
Definition blend.h:288
GtkDarktableToggleButton * off
Definition imageop.h:339
gpointer blend_data
Definition imageop.h:318
GHashTable * masks
Definition imageop.h:328
struct dt_develop_blend_params_t * blend_params
Definition imageop.h:316
struct dt_iop_module_t::@31 raster_mask
GtkWidget * widget
Definition imageop.h:337
char multi_name[128]
Definition imageop.h:363
struct dt_develop_t * dev
Definition imageop.h:296
struct dt_iop_module_t::@31::@32 source
GtkWidget * mask_indicator
Definition imageop.h:343
GModule *dt_dev_operation_t op
Definition imageop.h:256
int request_mask_display
Definition imageop.h:268
struct dt_iop_module_t::@31::@33 sink
GHashTable * users
Definition imageop.h:324
struct dt_develop_blend_params_t * default_blendop_params
Definition imageop.h:316
gboolean node_selected
Definition masks.h:470
gboolean handle_border_selected
Definition masks.h:473
gboolean source_selected
Definition masks.h:478
dt_masks_type_t creation_type
Definition masks.h:507
gboolean source_dragging
Definition masks.h:486
dt_masks_edit_mode_t edit_mode
Definition masks.h:463
dt_iop_module_t * creation_module
Definition masks.h:505
gboolean seg_selected
Definition masks.h:472
gboolean form_dragging
Definition masks.h:485
gboolean creation
Definition masks.h:503
gboolean form_selected
Definition masks.h:476
gboolean handle_selected
Definition masks.h:471
gboolean form_rotating
Definition masks.h:487
gboolean border_selected
Definition masks.h:477
gboolean pivot_selected
Definition masks.h:479
dt_masks_type_t type
Definition masks.h:378
char name[128]
Definition masks.h:396
GList * points
Definition masks.h:377
dt_iop_module_t * owner_module
Definition masks.h:969
Definition blend_gui.c:4040
dt_iop_module_t *int id
Definition blend_gui.c:4042
void dtgtk_togglebutton_set_paint(GtkDarktableToggleButton *button, DTGTKCairoPaintIconFunc paint, gint paintflags, void *paintdata)
GtkWidget * dtgtk_togglebutton_new(DTGTKCairoPaintIconFunc paint, gint paintflags, void *paintdata)
#define DTGTK_TOGGLEBUTTON(obj)
char * dt_get_help_url(char *name)