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(dt_gui_widgets_suppressed()) 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(dt_gui_widgets_suppressed()) 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(dt_gui_widgets_suppressed()) 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(dt_gui_widgets_suppressed()) 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(dt_gui_widgets_suppressed()) 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(dt_gui_widgets_suppressed()) 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(dt_gui_widgets_suppressed()) 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
1083}
1084
1085
1086static void _blendop_blendif_tab_switch(GtkNotebook *notebook, GtkWidget *page, guint page_num,
1088{
1089 if(dt_gui_widgets_suppressed() || 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(dt_gui_widgets_suppressed() || 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(dt_gui_widgets_suppressed() || 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(dt_gui_widgets_suppressed()) 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
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),
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(dt_gui_widgets_suppressed()) 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(dt_gui_widgets_suppressed()) 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(dt_gui_widgets_suppressed()) return FALSE;
1365
1366 if(event->button == 1)
1367 {
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
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(dt_gui_widgets_suppressed()) 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(dt_gui_widgets_suppressed()) 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(dt_gui_widgets_suppressed()) 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 group_form = dt_masks_cow_touch(darktable.develop, group_form);
1983 dt_masks_group_add_form(group_form, mask_form);
1985 }
1986 }
1987
1988 // If this is a group with shapes, update sensitive state of child shapes
1990 {
1991 const gboolean new_active = !active; // Toggle state
1992 GtkTreeIter search_iter;
1993 gboolean valid = gtk_tree_model_get_iter_first(model, &search_iter);
1994
1995 while(valid)
1996 {
1997 int child_formid = -1;
1998 gtk_tree_model_get(model, &search_iter, BLENDOP_MASKS_ALL_COL_FORMID, &child_formid, -1);
1999
2000 // Check if this child belongs to the toggled group
2001 if(_blendop_masks_find_group_entry(mask_form, child_formid, NULL))
2002 {
2003 // Lock (gray out) if group is being activated, unlock if deactivated
2004 gtk_list_store_set(GTK_LIST_STORE(model), &search_iter,
2005 BLENDOP_MASKS_ALL_COL_SENSITIVE, !new_active, -1);
2006 }
2007
2008 valid = gtk_tree_model_iter_next(model, &search_iter);
2009 }
2010 }
2011
2013
2014 const int parentid_after = group_form ? group_form->formid : parentid_before;
2016 active ? parentid_before : parentid_after,
2018}
2019
2020// Permanently delete a shape from the "all shapes" list: shared by the row's trash icon and
2021// the right-click "Delete" context menu entry, both gated by dt_masks_gui_confirm_permanent_delete().
2022static void _blendop_masks_all_delete(dt_iop_module_t *module, const int formid)
2023{
2024 if(IS_NULL_PTR(module)) return;
2025
2027 if(IS_NULL_PTR(mask_form)) return;
2028
2029 if(!dt_masks_gui_confirm_permanent_delete(mask_form->name)) return;
2030
2032 dt_masks_form_delete(module, NULL, mask_form);
2035}
2036
2038{
2039 if(IS_NULL_PTR(module)) return;
2040
2041 const int formid = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(menu_item), "blend-formid"));
2042 _blendop_masks_all_delete(module, formid);
2043}
2044
2046{
2047 if(IS_NULL_PTR(module)) return;
2048
2049 const int formid = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(menu_item), "blend-formid"));
2050 if(dt_masks_form_duplicate(darktable.develop, formid) <= 0) return;
2051
2054}
2055
2057{
2058 if(IS_NULL_PTR(module)) return;
2059 if(IS_NULL_PTR(module->blend_data)) return;
2060
2061 const int formid = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(menu_item), "blend-formid"));
2063 GtkTreeView *view = GTK_TREE_VIEW(bd->masks_treeview);
2064 GtkTreeModel *model = gtk_tree_view_get_model(view);
2065 if(!GTK_IS_TREE_MODEL(model)) return;
2066
2067 GtkTreeIter iter;
2069
2070 GtkTreePath *path = gtk_tree_model_get_path(model, &iter);
2071 GtkTreeViewColumn *column = gtk_tree_view_get_column(view, 1);
2072 GtkCellRenderer *renderer = g_object_get_data(G_OBJECT(view), "blendop-masks-name-renderer");
2073 if(path && column && renderer)
2074 gtk_tree_view_set_cursor_on_cell(view, path, column, renderer, TRUE);
2075 if(path) gtk_tree_path_free(path);
2076}
2077
2078static gboolean _blendop_masks_all_handle_left_click(GtkWidget *treeview, GtkTreePath *path,
2079 GtkTreeViewColumn *column, dt_iop_module_t *module)
2080{
2081 if(IS_NULL_PTR(module)) return FALSE;
2082 if(IS_NULL_PTR(path) || IS_NULL_PTR(column)) return FALSE;
2083
2085 if(IS_NULL_PTR(bd) || column != bd->all_shapes_col) return FALSE;
2086
2087 GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(treeview));
2088 GtkTreeIter iter;
2089 if(!gtk_tree_model_get_iter(model, &iter, path)) return FALSE;
2090
2091 gboolean sensitive = TRUE;
2092 int formid = -1;
2093 gtk_tree_model_get(model, &iter, BLENDOP_MASKS_ALL_COL_SENSITIVE, &sensitive,
2094 BLENDOP_MASKS_ALL_COL_FORMID, &formid, -1);
2095
2096 if(formid <= 0) return FALSE;
2097
2098 if(!sensitive)
2099 {
2100 gtk_tree_path_free(path);
2101 return TRUE;
2102 }
2103
2104 // Toggle the checkbox value.
2105 gchar *path_string = gtk_tree_path_to_string(path);
2106 _blendop_masks_all_toggled(NULL, path_string, module);
2107 dt_free(path_string);
2108 gtk_tree_path_free(path);
2109 return TRUE; // Block default selection behavior.
2110}
2111
2112static gboolean _blendop_masks_all_button_pressed(GtkWidget *treeview, GdkEventButton *event,
2113 dt_iop_module_t *module)
2114{
2115 if(IS_NULL_PTR(event) || event->type != GDK_BUTTON_PRESS) return FALSE;
2116
2117 GtkTreePath *path = NULL;
2118 GtkTreeViewColumn *column = NULL;
2119 if(!gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(treeview), (gint)event->x, (gint)event->y, &path,
2120 &column, NULL, NULL))
2121 return FALSE;
2122
2123 // Handle left click on the per-row delete icon column.
2124 if(event->button == GDK_BUTTON_PRIMARY && !IS_NULL_PTR(column))
2125 {
2127 if(!IS_NULL_PTR(bd) && column == bd->all_shapes_delete_col)
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) _blendop_masks_all_delete(module, formid);
2136 return TRUE;
2137 }
2138 }
2139
2140 // Handle left click on checkbox column - toggle directly without requiring selection
2141 if(event->button == GDK_BUTTON_PRIMARY && !IS_NULL_PTR(column))
2142 {
2143 if(_blendop_masks_all_handle_left_click(treeview, path, column, module))
2144 return TRUE;
2145 }
2146
2147 // Handle right click for context menu
2148 if(event->button != GDK_BUTTON_SECONDARY)
2149 {
2150 gtk_tree_path_free(path);
2151 return FALSE;
2152 }
2153
2154 GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview));
2155 gtk_tree_selection_unselect_all(selection);
2156 gtk_tree_selection_select_path(selection, path);
2157
2158 GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(treeview));
2159 GtkTreeIter iter;
2160 int formid = -1;
2161 if(gtk_tree_model_get_iter(model, &iter, path))
2162 gtk_tree_model_get(model, &iter, BLENDOP_MASKS_ALL_COL_FORMID, &formid, -1);
2163 gtk_tree_path_free(path);
2164 if(formid <= 0) return TRUE;
2165
2166 GtkWidget *menu = gtk_menu_new();
2167 GtkWidget *item = gtk_menu_item_new_with_label(_("Delete"));
2168 g_object_set_data(G_OBJECT(item), "blend-formid", GINT_TO_POINTER(formid));
2169 g_signal_connect(item, "activate", G_CALLBACK(_blendop_masks_all_delete_callback), module);
2170 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
2171
2172 item = gtk_menu_item_new_with_label(_("Duplicate"));
2173 g_object_set_data(G_OBJECT(item), "blend-formid", GINT_TO_POINTER(formid));
2174 g_signal_connect(item, "activate", G_CALLBACK(_blendop_masks_all_duplicate_callback), module);
2175 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
2176
2177 item = gtk_menu_item_new_with_label(_("Rename"));
2178 g_object_set_data(G_OBJECT(item), "blend-formid", GINT_TO_POINTER(formid));
2179 g_signal_connect(item, "activate", G_CALLBACK(_blendop_masks_all_rename_callback), module);
2180 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
2181
2182 gtk_widget_show_all(menu);
2183 gtk_menu_popup_at_pointer(GTK_MENU(menu), (GdkEvent *)event);
2184 return TRUE;
2185}
2186
2187static void _blendop_masks_group_operation_callback(GtkWidget *menu_item, gpointer user_data)
2188{
2189 dt_iop_module_t *module = (dt_iop_module_t *)user_data;
2190 if(IS_NULL_PTR(module)) return;
2191
2192 const int formid = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(menu_item), "blend-formid"));
2193 const int parentid = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(menu_item), "blend-parentid"));
2194 const dt_masks_state_t state = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(menu_item), "blend-state"));
2195
2196 // apply_operation() below mutates the group_entry in place, memory owned by the parent
2197 // group -- touch the parent before resolving the entry, or a shared parent's group_entry
2198 // gets mutated behind the back of a snapshot that still references it.
2199 dt_masks_form_t *parent_form = dt_masks_get_from_id(darktable.develop, parentid);
2200 if(IS_NULL_PTR(parent_form) || !(parent_form->type & DT_MASKS_GROUP)) return;
2201 parent_form = dt_masks_cow_touch(darktable.develop, parent_form);
2202
2203 dt_masks_form_group_t *group_entry = NULL;
2204 for(GList *group_node = parent_form->points; group_node; group_node = g_list_next(group_node))
2205 {
2206 dt_masks_form_group_t *entry = (dt_masks_form_group_t *)group_node->data;
2207 if(entry && entry->formid == formid)
2208 {
2209 group_entry = entry;
2210 break;
2211 }
2212 }
2213 if(IS_NULL_PTR(group_entry)) return;
2214
2215 const int old_state = group_entry->state;
2216 apply_operation(group_entry, state);
2217 if(group_entry->state == old_state) return;
2218
2222}
2223
2224static void _blendop_masks_group_selection_changed(GtkTreeSelection *selection, dt_iop_module_t *module)
2225{
2226 if(dt_gui_widgets_suppressed()) return;
2227 if(IS_NULL_PTR(module)) return;
2228 if(IS_NULL_PTR(selection)) return;
2229
2230 GtkTreeModel *model = NULL;
2231 GtkTreeIter iter;
2232 if(!gtk_tree_selection_get_selected(selection, &model, &iter)) return;
2233 if(!GTK_IS_TREE_MODEL(model)) return;
2234
2235 int formid = -1;
2236 gtk_tree_model_get(model, &iter, BLENDOP_MASKS_GROUP_COL_FORMID, &formid, -1);
2237 if(formid <= 0) return;
2239 if(IS_NULL_PTR(mask_form)) return;
2240
2241 // Switching edit mode rebuilds the visible module group. Do it only when the
2242 // current overlay does not already contain the selected row, because this
2243 // callback runs inside a GtkTreeSelection::changed emission.
2244 dt_masks_form_gui_t *gui = module->dev ? module->dev->form_gui : NULL;
2245 dt_masks_form_t *visible_form = module->dev ? dt_masks_get_visible_form(module->dev) : NULL;
2246 if(IS_NULL_PTR(gui) || gui->edit_mode != DT_MASKS_EDIT_FULL
2247 || dt_masks_group_index_from_formid(visible_form, formid) < 0)
2248 {
2249 // Rebuilding the visible group may update Gtk widgets. Hold the GUI reset
2250 // guard so selection callbacks emitted during that rebuild don't recurse.
2254 }
2255
2256 // Keep lib/masks tree selection in sync without re-triggering its selection handler,
2257 // otherwise the visible overlay can be replaced by only the clicked shape.
2259
2260 // Mark the selected shape as active in the central mask GUI state.
2261 if(module->dev && module->dev->form_gui)
2262 {
2263 gui = module->dev->form_gui;
2264 visible_form = dt_masks_get_visible_form(module->dev);
2265 const int selected_index = dt_masks_group_index_from_formid(visible_form, formid);
2266 if(selected_index >= 0)
2267 {
2268 gui->group_selected = selected_index;
2269 gui->form_selected = TRUE;
2270 gui->border_selected = FALSE;
2271 gui->source_selected = FALSE;
2272 gui->node_selected = FALSE;
2273 gui->handle_selected = FALSE;
2274 gui->seg_selected = FALSE;
2276 gui->node_selected_idx = -1;
2277 gui->form_dragging = FALSE;
2278 gui->source_dragging = FALSE;
2279 gui->form_rotating = FALSE;
2280 gui->pivot_selected = FALSE;
2281 }
2282 }
2283 dt_masks_center_view_on_form(module->dev, mask_form);
2284}
2285
2286static gboolean _blendop_masks_group_move_by_index(dt_masks_form_t *group_form, const int index,
2287 const gboolean move_up)
2288{
2289 if(IS_NULL_PTR(group_form)) return FALSE;
2290 if(!(group_form->type & DT_MASKS_GROUP)) return FALSE;
2291 if(index < 0) return FALSE;
2292
2293 const guint length = g_list_length(group_form->points);
2294 if(length == 0 || (guint)index >= length) return FALSE;
2295 if(move_up && index == 0) return FALSE;
2296 if(!move_up && (guint)index >= length - 1) return FALSE;
2297
2298 // Touch before resolving `entry`: cloning group_form also clones its points (fresh
2299 // dt_masks_form_group_t blocks), so an entry resolved from the pre-touch list would not
2300 // be a member of the (possibly cloned) list touched below.
2301 group_form = dt_masks_cow_touch(darktable.develop, group_form);
2302 dt_masks_form_group_t *entry = (dt_masks_form_group_t *)g_list_nth_data(group_form->points, index);
2303 if(IS_NULL_PTR(entry)) return FALSE;
2304
2305 const int new_index = move_up ? index - 1 : index + 1;
2306 group_form->points = g_list_remove(group_form->points, entry);
2307 group_form->points = g_list_insert(group_form->points, entry, new_index);
2308 return TRUE;
2309}
2310
2312{
2313 if(IS_NULL_PTR(module)) return;
2314
2315 const int parentid = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(menu_item), "blend-parentid"));
2316 const int index = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(menu_item), "blend-index"));
2317 const gboolean move_up = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(menu_item), "blend-move-up"));
2318 dt_masks_form_t *group_form = dt_masks_get_from_id(darktable.develop, parentid);
2319 if(!_blendop_masks_group_move_by_index(group_form, index, move_up)) return;
2320
2324}
2325
2326static void _blendop_masks_edit_list_toggle(GtkToggleButton *togglebutton, dt_iop_module_t *module)
2327{
2329
2330 if(!GTK_IS_TOGGLE_BUTTON(togglebutton)) return;
2331
2332 // The edit toggle swaps the visible list between:
2333 // - current module mask tree
2334 // - global all-shapes list
2335 const gboolean edit_mode = gtk_toggle_button_get_active(togglebutton);
2336
2337 if(edit_mode)
2338 gtk_button_set_label(GTK_BUTTON(togglebutton), _("OK"));
2339 else
2340 gtk_button_set_label(GTK_BUTTON(togglebutton), _("Attach shapes"));
2341
2342 if(GTK_IS_STACK(bd->lists_stack))
2343 gtk_stack_set_visible_child_name(GTK_STACK(bd->lists_stack), edit_mode ? "all" : "group");
2344
2345 if(GTK_IS_ENTRY(bd->group_shapes_label))
2346 gtk_widget_set_sensitive(bd->group_shapes_label, !edit_mode);
2347}
2348
2350{
2351 if(IS_NULL_PTR(module) || IS_NULL_PTR(bd)) return NULL;
2352
2353 const dt_masks_shape_buttons_config_t config = {
2354 .owner_module = module,
2355 .creation_module = module,
2356 .buttons = bd->masks_shapes,
2357 .types = bd->masks_type,
2358 .action_section = "blend_shapes",
2360 .register_flags = DT_MASKS_SHAPE_BUTTONS_POLYGON,
2361 .local = FALSE,
2362 .user_data = bd,
2363 .can_start = _blendop_masks_shape_can_start,
2364 .form_type = NULL,
2365 .started = NULL,
2366 .exited = NULL
2367 };
2368 GtkWidget *widget = dt_masks_shape_buttons_create(&config);
2369
2370 return widget;
2371}
2372
2374 const int formid, const int parentid, const int state,
2375 const int index, const int list_length)
2376{
2377 if(IS_NULL_PTR(bd) || IS_NULL_PTR(module)) return NULL;
2378
2379 // Initialize mask operation icons if needed
2381
2382 GtkWidget *menu = gtk_menu_new();
2383 gtk_style_context_add_class(gtk_widget_get_style_context(menu), "dt-masks-context-menu");
2384
2385 GtkWidget *op_item = gtk_menu_item_new_with_label(_("Operation"));
2386 GtkWidget *op_submenu = gtk_menu_new();
2387 gtk_menu_item_set_submenu(GTK_MENU_ITEM(op_item), op_submenu);
2388 gtk_menu_shell_append(GTK_MENU_SHELL(menu), op_item);
2389
2391 op_submenu,
2393 module,
2395 g_object_set_data(G_OBJECT(item), "blend-formid", GINT_TO_POINTER(formid));
2396 g_object_set_data(G_OBJECT(item), "blend-parentid", GINT_TO_POINTER(parentid));
2397 g_object_set_data(G_OBJECT(item), "blend-state", GINT_TO_POINTER(DT_MASKS_STATE_INVERSE));
2398
2399 gtk_menu_shell_append(GTK_MENU_SHELL(op_submenu), gtk_separator_menu_item_new());
2400
2401 item = ctx_gtk_check_menu_item_new_with_markup_and_pixbuf(_("Union"), bd->masks_ic_union, op_submenu,
2403 (state & DT_MASKS_STATE_UNION) != 0, FALSE);
2404 gtk_widget_set_sensitive(item, index > 0);
2405 g_object_set_data(G_OBJECT(item), "blend-formid", GINT_TO_POINTER(formid));
2406 g_object_set_data(G_OBJECT(item), "blend-parentid", GINT_TO_POINTER(parentid));
2407 g_object_set_data(G_OBJECT(item), "blend-state", GINT_TO_POINTER(DT_MASKS_STATE_UNION));
2408
2409 item = ctx_gtk_check_menu_item_new_with_markup_and_pixbuf(_("Intersection"), bd->masks_ic_intersection, op_submenu,
2412 gtk_widget_set_sensitive(item, index > 0);
2413 g_object_set_data(G_OBJECT(item), "blend-formid", GINT_TO_POINTER(formid));
2414 g_object_set_data(G_OBJECT(item), "blend-parentid", GINT_TO_POINTER(parentid));
2415 g_object_set_data(G_OBJECT(item), "blend-state", GINT_TO_POINTER(DT_MASKS_STATE_INTERSECTION));
2416
2417 item = ctx_gtk_check_menu_item_new_with_markup_and_pixbuf(_("Difference"), bd->masks_ic_difference, op_submenu,
2420 gtk_widget_set_sensitive(item, index > 0);
2421 g_object_set_data(G_OBJECT(item), "blend-formid", GINT_TO_POINTER(formid));
2422 g_object_set_data(G_OBJECT(item), "blend-parentid", GINT_TO_POINTER(parentid));
2423 g_object_set_data(G_OBJECT(item), "blend-state", GINT_TO_POINTER(DT_MASKS_STATE_DIFFERENCE));
2424
2425 item = ctx_gtk_check_menu_item_new_with_markup_and_pixbuf(_("Exclusion"), bd->masks_ic_exclusion, op_submenu,
2428 gtk_widget_set_sensitive(item, index > 0);
2429 g_object_set_data(G_OBJECT(item), "blend-formid", GINT_TO_POINTER(formid));
2430 g_object_set_data(G_OBJECT(item), "blend-parentid", GINT_TO_POINTER(parentid));
2431 g_object_set_data(G_OBJECT(item), "blend-state", GINT_TO_POINTER(DT_MASKS_STATE_EXCLUSION));
2432
2433 gtk_menu_shell_append(GTK_MENU_SHELL(menu), gtk_separator_menu_item_new());
2434
2435 item = gtk_menu_item_new_with_label(_("Move Up"));
2436 gtk_widget_set_sensitive(item, index > 0);
2437 g_object_set_data(G_OBJECT(item), "blend-parentid", GINT_TO_POINTER(parentid));
2438 g_object_set_data(G_OBJECT(item), "blend-index", GINT_TO_POINTER(index));
2439 g_object_set_data(G_OBJECT(item), "blend-move-up", GINT_TO_POINTER(TRUE));
2440 g_signal_connect(item, "activate", G_CALLBACK(_blendop_masks_group_move_callback), module);
2441 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
2442
2443 item = gtk_menu_item_new_with_label(_("Move Down"));
2444 gtk_widget_set_sensitive(item, index >= 0 && index < list_length - 1);
2445 g_object_set_data(G_OBJECT(item), "blend-parentid", GINT_TO_POINTER(parentid));
2446 g_object_set_data(G_OBJECT(item), "blend-index", GINT_TO_POINTER(index));
2447 g_object_set_data(G_OBJECT(item), "blend-move-up", GINT_TO_POINTER(FALSE));
2448 g_signal_connect(item, "activate", G_CALLBACK(_blendop_masks_group_move_callback), module);
2449 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
2450
2451 return menu;
2452}
2453
2454// Detach the form from its parent group, but keep it in dev->forms for potential reuse.
2455static void _blendop_masks_group_unlink(dt_iop_module_t *module, const int formid, const int parentid)
2456{
2457 if(IS_NULL_PTR(module)) return;
2458 dt_masks_form_t *parent_group = dt_masks_get_from_id(darktable.develop, parentid);
2460 if(IS_NULL_PTR(parent_group) || !(parent_group->type & DT_MASKS_GROUP) || IS_NULL_PTR(form)) return;
2461
2462 // Discard any visible overlay before mutating the group.
2464
2465 // Passing the parent group only detaches the form from this group.
2466 dt_masks_form_delete(module, parent_group, form);
2467
2472}
2473
2474// Detach the form from its parent group and remove it from dev->forms altogether.
2475static void _blendop_masks_group_delete(dt_iop_module_t *module, const int formid, const int parentid)
2476{
2477 if(IS_NULL_PTR(module)) return;
2479 if(IS_NULL_PTR(form)) return;
2480
2482
2483 // Discard any visible overlay before mutating the masks.
2485
2486 // Passing no group permanently deletes the form from every module and dev->forms.
2487 dt_masks_form_delete(module, NULL, form);
2488
2493}
2494
2495// Handle a left click on the per-row unlink / delete icon columns.
2496// Returns TRUE if the click was consumed (so row selection is not triggered).
2497static gboolean _blendop_masks_group_handle_action_click(GtkWidget *treeview, GtkTreePath *path,
2498 GtkTreeViewColumn *column, dt_iop_module_t *module)
2499{
2500 if(IS_NULL_PTR(module) || IS_NULL_PTR(path) || IS_NULL_PTR(column)) return FALSE;
2502 if(IS_NULL_PTR(bd)) return FALSE;
2503 if(column != bd->group_unlink_col && column != bd->group_delete_col) return FALSE;
2504
2505 GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(treeview));
2506 GtkTreeIter iter;
2507 if(!gtk_tree_model_get_iter(model, &iter, path)) return TRUE;
2508
2509 int formid = -1;
2510 int parentid = -1;
2511 gtk_tree_model_get(model, &iter, BLENDOP_MASKS_GROUP_COL_FORMID, &formid,
2512 BLENDOP_MASKS_GROUP_COL_PARENTID, &parentid, -1);
2513 if(formid <= 0 || parentid <= 0) return TRUE;
2514
2515 if(column == bd->group_unlink_col)
2516 _blendop_masks_group_unlink(module, formid, parentid);
2517 else
2518 _blendop_masks_group_delete(module, formid, parentid);
2519
2520 return TRUE;
2521}
2522
2523static gboolean _blendop_masks_group_button_pressed(GtkWidget *treeview, GdkEventButton *event,
2524 dt_iop_module_t *module)
2525{
2526 if(IS_NULL_PTR(event) || event->type != GDK_BUTTON_PRESS) return FALSE;
2527
2528 GtkTreePath *path = NULL;
2529 GtkTreeViewColumn *column = NULL;
2530 if(!gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(treeview), (gint)event->x, (gint)event->y, &path,
2531 &column, NULL, NULL))
2532 return FALSE;
2533
2534 // Left click on the unlink / delete icon columns triggers the row action.
2535 if(event->button == GDK_BUTTON_PRIMARY)
2536 {
2537 const gboolean consumed = _blendop_masks_group_handle_action_click(treeview, path, column, module);
2538 gtk_tree_path_free(path);
2539 return consumed;
2540 }
2541
2542 if(event->button != GDK_BUTTON_SECONDARY)
2543 {
2544 gtk_tree_path_free(path);
2545 return FALSE;
2546 }
2547
2548 GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview));
2549 gtk_tree_selection_unselect_all(selection);
2550 gtk_tree_selection_select_path(selection, path);
2551
2552 GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(treeview));
2553 GtkTreeIter iter;
2554 int formid = -1;
2555 int parentid = -1;
2556 int state = 0;
2557 int index = -1;
2558 if(gtk_tree_model_get_iter(model, &iter, path))
2559 gtk_tree_model_get(model, &iter, BLENDOP_MASKS_GROUP_COL_FORMID, &formid,
2562 BLENDOP_MASKS_GROUP_COL_INDEX, &index, -1);
2563 gtk_tree_path_free(path);
2564
2565 if(formid <= 0 || parentid <= 0) return TRUE;
2566
2568 dt_masks_form_t *parent_group = dt_masks_get_from_id(darktable.develop, parentid);
2569 const int list_length = (parent_group && (parent_group->type & DT_MASKS_GROUP))
2570 ? g_list_length(parent_group->points)
2571 : 0;
2572
2573 GtkWidget *menu = _blendop_masks_group_ctx_menu(bd, module, formid, parentid, state, index, list_length);
2574 if(IS_NULL_PTR(menu)) return TRUE;
2575 gtk_widget_show_all(menu);
2576 gtk_menu_popup_at_pointer(GTK_MENU(menu), (GdkEvent *)event);
2577 return TRUE;
2578}
2579
2580// Per-column tooltips for the unlink / delete action icons.
2581static gboolean _blendop_masks_group_query_tooltip(GtkWidget *treeview, gint x, gint y, gboolean keyboard_tip,
2582 GtkTooltip *tooltip, dt_iop_module_t *module)
2583{
2584 if(IS_NULL_PTR(module) || keyboard_tip) return FALSE;
2586 if(IS_NULL_PTR(bd)) return FALSE;
2587
2588 gint bx = 0, by = 0;
2589 gtk_tree_view_convert_widget_to_bin_window_coords(GTK_TREE_VIEW(treeview), x, y, &bx, &by);
2590
2591 GtkTreePath *path = NULL;
2592 GtkTreeViewColumn *column = NULL;
2593 if(!gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(treeview), bx, by, &path, &column, NULL, NULL))
2594 return FALSE;
2595 gtk_tree_path_free(path);
2596
2597 const char *text = NULL;
2598 if(column == bd->group_unlink_col)
2599 text = _("Detach this shape from the mask. The shape is kept and stays available for reuse.");
2600 else if(column == bd->group_delete_col)
2601 text = _("Permanently delete this shape. It is detached from the mask and removed from the list of available shapes.");
2602
2603 if(IS_NULL_PTR(text)) return FALSE;
2604
2605 gtk_tooltip_set_text(tooltip, text);
2606 return TRUE;
2607}
2608
2609// Depth-first search for the row matching (formid, parentid) anywhere in the group tree.
2610static gboolean _blendop_masks_group_find_row(GtkTreeModel *model, GtkTreeIter *parent,
2611 const int formid, const int parentid, GtkTreeIter *out)
2612{
2613 GtkTreeIter iter;
2614 gboolean valid = gtk_tree_model_iter_children(model, &iter, parent);
2615 while(valid)
2616 {
2617 int fid = -1;
2618 int pid = -1;
2619 gtk_tree_model_get(model, &iter, BLENDOP_MASKS_GROUP_COL_FORMID, &fid,
2621 if(fid == formid && pid == parentid)
2622 {
2623 *out = iter;
2624 return TRUE;
2625 }
2626 if(gtk_tree_model_iter_has_child(model, &iter)
2627 && _blendop_masks_group_find_row(model, &iter, formid, parentid, out))
2628 return TRUE;
2629 valid = gtk_tree_model_iter_next(model, &iter);
2630 }
2631 return FALSE;
2632}
2633
2634// Refresh a single row in place (name + opacity + operation/inverse icons), the same way
2635// the masks library treeview does, so live opacity changes don't rebuild and collapse the tree.
2636static void _blendop_masks_group_update_row(dt_iop_module_t *module, const int formid, const int parentid)
2637{
2638 if(IS_NULL_PTR(module) || IS_NULL_PTR(module->blend_data)) return;
2640 if(!GTK_IS_TREE_VIEW(bd->masks_group_treeview)) return;
2641 GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(bd->masks_group_treeview));
2642 if(!GTK_IS_TREE_STORE(model)) return;
2643
2644 GtkTreeIter iter;
2645 if(!_blendop_masks_group_find_row(model, NULL, formid, parentid, &iter)) return;
2646
2648 dt_masks_form_t *parent_group = dt_masks_get_from_id(darktable.develop, parentid);
2649 int index = -1;
2650 dt_masks_form_group_t *entry = _blendop_masks_find_group_entry(parent_group, formid, &index);
2651 if(IS_NULL_PTR(form) || IS_NULL_PTR(entry)) return;
2652
2653 char display_name[256] = "";
2654 if(entry->opacity != 1.0f)
2655 g_snprintf(display_name, sizeof(display_name), "%s %d%%", form->name, (int)(entry->opacity * 100));
2656 else
2657 g_strlcpy(display_name, form->name, sizeof(display_name));
2658
2659 gtk_tree_store_set(GTK_TREE_STORE(model), &iter, BLENDOP_MASKS_GROUP_COL_NAME, display_name,
2663}
2664
2665static void _blendop_masks_handler_callback(gpointer instance, const int formid, const int parentid,
2666 const dt_masks_event_t event, dt_iop_module_t *module)
2667{
2668 (void)instance;
2669 // A plain value update (e.g. opacity) only needs the affected row refreshed in place.
2670 // Structural changes rebuild the whole list.
2671 if(event == DT_MASKS_EVENT_UPDATE)
2672 _blendop_masks_group_update_row(module, formid, parentid);
2673 else
2675}
2676
2679{
2680 dt_iop_gui_blend_data_t *data = module->blend_data;
2681
2682 if(picker == data->colorpicker_set_values)
2683 {
2684 if(dt_gui_widgets_suppressed()) return TRUE;
2685
2687
2688 dt_develop_blend_params_t *bp = module->blend_params;
2689
2690 const int tab = data->tab;
2691 dt_aligned_pixel_t raw_min, raw_max;
2692 float picker_min[8] DT_ALIGNED_PIXEL, picker_max[8] DT_ALIGNED_PIXEL;
2693 dt_aligned_pixel_t picker_values;
2694
2695 const int in_out = ((dt_key_modifier_state() == GDK_CONTROL_MASK) && data->output_channels_shown) ? 1 : 0;
2696
2697 if(in_out)
2698 {
2699 for(size_t i = 0; i < 4; i++)
2700 {
2701 raw_min[i] = module->picked_output_color_min[i];
2702 raw_max[i] = module->picked_output_color_max[i];
2703 }
2704 }
2705 else
2706 {
2707 for(size_t i = 0; i < 4; i++)
2708 {
2709 raw_min[i] = module->picked_color_min[i];
2710 raw_max[i] = module->picked_color_max[i];
2711 }
2712 }
2713
2714 const dt_iop_gui_blendif_channel_t *channel = &data->channel[data->tab];
2716 dt_iop_gui_blendif_filter_t *sl = &data->filter[in_out];
2717
2718 float *parameters = &(bp->blendif_parameters[4 * ch]);
2719
2720 const dt_develop_blend_colorspace_t blend_csp = data->channel_tabs_csp;
2722 const dt_iop_order_iccprofile_info_t *work_profile = (blend_csp == DEVELOP_BLEND_CS_RGB_SCENE)
2723 ? dt_ioppr_get_pipe_current_profile_info(piece->module, pipe)
2724 : dt_ioppr_get_iop_work_profile_info(module, module->dev->iop);
2725
2726 gboolean reverse_hues = FALSE;
2727 if(cst == IOP_CS_HSL && tab == CHANNEL_INDEX_H)
2728 {
2729 if((raw_max[3] - raw_min[3]) < (raw_max[0] - raw_min[0]) && raw_min[3] < 0.5f && raw_max[3] > 0.5f)
2730 {
2731 raw_max[0] = raw_max[3] < 0.5f ? raw_max[3] + 0.5f : raw_max[3] - 0.5f;
2732 raw_min[0] = raw_min[3] < 0.5f ? raw_min[3] + 0.5f : raw_min[3] - 0.5f;
2733 reverse_hues = TRUE;
2734 }
2735 }
2736 else if((cst == IOP_CS_LCH && tab == CHANNEL_INDEX_h) || (cst == IOP_CS_JZCZHZ && tab == CHANNEL_INDEX_hz))
2737 {
2738 if((raw_max[3] - raw_min[3]) < (raw_max[2] - raw_min[2]) && raw_min[3] < 0.5f && raw_max[3] > 0.5f)
2739 {
2740 raw_max[2] = raw_max[3] < 0.5f ? raw_max[3] + 0.5f : raw_max[3] - 0.5f;
2741 raw_min[2] = raw_min[3] < 0.5f ? raw_min[3] + 0.5f : raw_min[3] - 0.5f;
2742 reverse_hues = TRUE;
2743 }
2744 }
2745
2746 _blendif_scale(data, cst, raw_max, picker_max, work_profile, in_out);
2747
2748 gboolean picker_box_changed = FALSE;
2749 const dt_colorpicker_sample_t *const sample = module->dev->color_picker.primary_sample;
2750 if(!IS_NULL_PTR(sample) && sample->size == DT_LIB_COLORPICKER_SIZE_BOX)
2751 {
2753 {
2754 picker_box_changed = TRUE;
2755 }
2756 else
2757 {
2758 const float epsilon = 1e-6f;
2759 for(int k = 0; k < 4; k++)
2760 {
2761 if(fabsf(sample->box[k] - data->picker_set_values_box[k]) > epsilon)
2762 {
2763 picker_box_changed = TRUE;
2764 break;
2765 }
2766 }
2767 }
2768
2769 if(picker_box_changed)
2770 {
2771 for(int k = 0; k < 4; k++) data->picker_set_values_box[k] = sample->box[k];
2774 }
2775 }
2776 else
2777 {
2780 }
2781
2793 if(channel->boost_factor_enabled && picker_box_changed
2795 {
2796 const float picker_margin = 0.02f;
2797 const float boost_step = 0.25f;
2798 const int max_boost_iters = 64;
2799 const float boost_min = channel->boost_factor_offset;
2800 const float boost_max = channel->boost_factor_offset + 18.0f;
2801 const float target_max = (picker_max[tab] > (1.0f - picker_margin)) ? 1.0f : (1.0f - picker_margin);
2802 float trial = bp->blendif_boost_factors[ch];
2803 gboolean within_bounds = picker_max[tab] <= target_max;
2804
2805 if(!within_bounds)
2806 {
2807 for(int iter = 0; iter < max_boost_iters && trial < boost_max; iter++)
2808 {
2809 trial = fminf(trial + boost_step, boost_max);
2810 bp->blendif_boost_factors[ch] = trial;
2811 _blendif_scale(data, cst, raw_max, picker_max, work_profile, in_out);
2812 within_bounds = picker_max[tab] <= target_max;
2813 if(within_bounds) break;
2814 }
2815 }
2816
2817 if(within_bounds)
2818 {
2819 for(int iter = 0; iter < max_boost_iters && trial > boost_min; iter++)
2820 {
2821 const float candidate = fmaxf(trial - boost_step, boost_min);
2822 bp->blendif_boost_factors[ch] = candidate;
2823 _blendif_scale(data, cst, raw_max, picker_max, work_profile, in_out);
2824 if(picker_max[tab] > target_max)
2825 break;
2826 trial = candidate;
2827 }
2828 }
2829
2830 bp->blendif_boost_factors[ch] = trial;
2831 _blendif_scale(data, cst, raw_max, picker_max, work_profile, in_out);
2832 }
2833
2834 _blendif_scale(data, cst, raw_min, picker_min, work_profile, in_out);
2835
2836 const float feather = 0.01f;
2837
2838 if(picker_min[tab] > picker_max[tab])
2839 {
2840 const float tmp = picker_min[tab];
2841 picker_min[tab] = picker_max[tab];
2842 picker_max[tab] = tmp;
2843 }
2844
2845 picker_values[0] = CLAMP(picker_min[tab] - feather, 0.f, 1.f);
2846 picker_values[1] = CLAMP(picker_min[tab] + feather, 0.f, 1.f);
2847 picker_values[2] = CLAMP(picker_max[tab] - feather, 0.f, 1.f);
2848 picker_values[3] = CLAMP(picker_max[tab] + feather, 0.f, 1.f);
2849
2850 if(picker_values[1] > picker_values[2])
2851 {
2852 picker_values[1] = CLAMP(picker_min[tab], 0.f, 1.f);
2853 picker_values[2] = CLAMP(picker_max[tab], 0.f, 1.f);
2854 }
2855
2856 picker_values[0] = CLAMP(picker_values[0], 0.f, picker_values[1]);
2857 picker_values[3] = CLAMP(picker_values[3], picker_values[2], 1.f);
2858
2860 for(int k = 0; k < 4; k++)
2863
2864 // update picked values
2865 _update_gradient_slider_pickers(NULL, module);
2866
2867 const float boost_factor = _get_boost_factor(data, data->tab, in_out);
2868 for(int k = 0; k < 4; k++)
2869 {
2870 char text[256];
2872 text, sizeof(text));
2873 gtk_label_set_text(sl->label[k], text);
2874 }
2875
2877
2878 // save values to parameters
2880 for(int k = 0; k < 4; k++)
2883
2884 // de-activate processing of this channel if maximum span is selected
2885 if(parameters[1] == 0.0f && parameters[2] == 1.0f)
2886 bp->blendif &= ~(1 << ch);
2887 else
2888 bp->blendif |= (1 << ch);
2889
2890 // set the polarity of the channel to include the picked values
2891 if(reverse_hues == ((bp->mask_combine & DEVELOP_COMBINE_INV) == DEVELOP_COMBINE_INV))
2892 bp->blendif &= ~(1 << (16 + ch));
2893 else
2894 bp->blendif |= 1 << (16 + ch);
2895
2897
2899 _blendop_blendif_update_tab(module, tab);
2900
2901 return TRUE;
2902 }
2903 else if(picker == data->colorpicker)
2904 {
2905 if(dt_gui_widgets_suppressed()) return TRUE;
2906
2907 _update_gradient_slider_pickers(NULL, module);
2908
2909 return TRUE;
2910 }
2911 else return FALSE; // needs to be handled by module
2912}
2913
2915{
2916 switch(cst)
2917 {
2922 break;
2923 default:
2925 break;
2926 }
2927 if(cst != module->blend_params->blend_cst)
2928 {
2930
2931 // look for last history item for this module with the selected blending mode to copy parametric mask settings
2932 for(const GList *history = g_list_last(darktable.develop->history); history; history = g_list_previous(history))
2933 {
2934 const dt_dev_history_item_t *data = (dt_dev_history_item_t *)(history->data);
2935 if(data->module == module && data->blend_params->blend_cst == cst)
2936 {
2937 const dt_develop_blend_params_t *hp = data->blend_params;
2938 dt_develop_blend_params_t *np = module->blend_params;
2939
2940 np->blend_mode = hp->blend_mode;
2942 np->blendif = hp->blendif;
2943 memcpy(np->blendif_parameters, hp->blendif_parameters, sizeof(hp->blendif_parameters));
2945 break;
2946 }
2947 }
2948
2949 dt_iop_gui_blend_data_t *bd = module->blend_data;
2950 const int cst_old = _blendop_blendif_get_picker_colorspace(bd);
2952 dt_iop_gui_update(module);
2953
2954 if(cst_old != _blendop_blendif_get_picker_colorspace(bd) &&
2955 (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(bd->colorpicker)) ||
2956 gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(bd->colorpicker_set_values))))
2957 {
2959 dt_dev_pixelpipe_update_history_all(bd->module->dev);
2960 }
2961
2962 return TRUE;
2963 }
2964 return FALSE;
2965}
2966
2967static void _blendif_select_colorspace(GtkMenuItem *menuitem, dt_iop_module_t *module)
2968{
2969 dt_develop_blend_colorspace_t cst = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(menuitem), "dt-blend-cst"));
2970 if(_blendif_change_blend_colorspace(module, cst))
2971 {
2972 gtk_widget_queue_draw(module->widget);
2973 }
2974}
2975
2976static void _blendif_show_output_channels(GtkMenuItem *menuitem, dt_iop_module_t *module)
2977{
2979 if(IS_NULL_PTR(bd) || !bd->blendif_support || !bd->blendif_inited) return;
2980 if(!bd->output_channels_shown)
2981 {
2983 dt_iop_gui_update(module);
2984 }
2985}
2986
2987static void _blendif_hide_output_channels(GtkMenuItem *menuitem, dt_iop_module_t *module)
2988{
2990 if(IS_NULL_PTR(bd) || !bd->blendif_support || !bd->blendif_inited) return;
2991 if(bd->output_channels_shown)
2992 {
2995 {
2997 }
2998 dt_iop_gui_update(module);
2999 }
3000}
3001
3002static void _blendif_options_callback(GtkButton *button, GdkEventButton *event, dt_iop_module_t *module)
3003{
3004 if(event->button != 1 && event->button != 2) return;
3006 if(IS_NULL_PTR(bd) || !bd->blendif_support || !bd->blendif_inited) return;
3007
3008 GtkWidget *mi;
3009 GtkMenu *menu = darktable.gui->presets_popup_menu;
3010 if(menu) gtk_widget_destroy(GTK_WIDGET(menu));
3011 darktable.gui->presets_popup_menu = GTK_MENU(gtk_menu_new());
3013
3014 // add a section to switch blending color spaces
3016 const dt_develop_blend_colorspace_t module_blend_cst = module->blend_params->blend_cst;
3017 if(module_cst == DEVELOP_BLEND_CS_LAB || module_cst == DEVELOP_BLEND_CS_RGB_DISPLAY
3018 || module_cst == DEVELOP_BLEND_CS_RGB_SCENE)
3019 {
3020
3021 mi = gtk_menu_item_new_with_label(_("reset to default blend colorspace"));
3022 g_object_set_data_full(G_OBJECT(mi), "dt-blend-cst", GINT_TO_POINTER(DEVELOP_BLEND_CS_NONE), NULL);
3023 g_signal_connect(G_OBJECT(mi), "activate", G_CALLBACK(_blendif_select_colorspace), module);
3024 gtk_menu_shell_append(GTK_MENU_SHELL(menu), mi);
3025
3026 // only show Lab blending when the module is a Lab module to avoid using it at the wrong place (Lab blending
3027 // should not be activated for RGB modules before colorin and after colorout)
3028 if(module_cst == DEVELOP_BLEND_CS_LAB)
3029 {
3030 mi = gtk_check_menu_item_new_with_label(_("Lab"));
3031
3032 if(module_blend_cst == DEVELOP_BLEND_CS_LAB)
3033 {
3034 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(mi), TRUE);
3035 dt_gui_add_class(mi, "active_menu_item");
3036 }
3037 g_object_set_data_full(G_OBJECT(mi), "dt-blend-cst", GINT_TO_POINTER(DEVELOP_BLEND_CS_LAB), NULL);
3038 g_signal_connect(G_OBJECT(mi), "activate", G_CALLBACK(_blendif_select_colorspace), module);
3039 gtk_menu_shell_append(GTK_MENU_SHELL(menu), mi);
3040 }
3041
3042 mi = gtk_check_menu_item_new_with_label(_("RGB (display)"));
3043
3044 if(module_blend_cst == DEVELOP_BLEND_CS_RGB_DISPLAY)
3045 {
3046 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(mi), TRUE);
3047 dt_gui_add_class(mi, "active_menu_item");
3048 }
3049 g_object_set_data_full(G_OBJECT(mi), "dt-blend-cst", GINT_TO_POINTER(DEVELOP_BLEND_CS_RGB_DISPLAY), NULL);
3050 g_signal_connect(G_OBJECT(mi), "activate", G_CALLBACK(_blendif_select_colorspace), module);
3051 gtk_menu_shell_append(GTK_MENU_SHELL(menu), mi);
3052
3053 mi = gtk_check_menu_item_new_with_label(_("RGB (scene)"));
3054
3055 if(module_blend_cst == DEVELOP_BLEND_CS_RGB_SCENE)
3056 {
3057 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(mi), TRUE);
3058 dt_gui_add_class(mi, "active_menu_item");
3059 }
3060 g_object_set_data_full(G_OBJECT(mi), "dt-blend-cst", GINT_TO_POINTER(DEVELOP_BLEND_CS_RGB_SCENE), NULL);
3061 g_signal_connect(G_OBJECT(mi), "activate", G_CALLBACK(_blendif_select_colorspace), module);
3062 gtk_menu_shell_append(GTK_MENU_SHELL(menu), mi);
3063
3064 gtk_menu_shell_append(GTK_MENU_SHELL(menu), gtk_separator_menu_item_new());
3065
3066 if(bd->output_channels_shown)
3067 {
3068 mi = gtk_menu_item_new_with_label(_("reset and hide output channels"));
3069 g_signal_connect(G_OBJECT(mi), "activate", G_CALLBACK(_blendif_hide_output_channels), module);
3070 gtk_menu_shell_append(GTK_MENU_SHELL(menu), mi);
3071 }
3072 else
3073 {
3074 mi = gtk_menu_item_new_with_label(_("show output channels"));
3075 g_signal_connect(G_OBJECT(mi), "activate", G_CALLBACK(_blendif_show_output_channels), module);
3076 gtk_menu_shell_append(GTK_MENU_SHELL(menu), mi);
3077 }
3078 }
3079
3080 dt_gui_menu_popup(darktable.gui->presets_popup_menu, GTK_WIDGET(button), GDK_GRAVITY_SOUTH_EAST, GDK_GRAVITY_NORTH_EAST);
3081
3083}
3084
3085// activate channel/mask view
3087{
3088 // FIXME : there are more than 3 functions getting clever about how to setup module->request_mask_display depending on user input.
3089 // These should all use an uniform setter function.
3090 //
3091 // The lack of setter implies we don't guaranty that only 1 module can request mask display at a time.
3092 // The consequence is pipeline needs to check if module->request_mask_display AND module == dev->gui_module,
3093 // but the global pipe->mask_display is set from *_blend_process() at runtime, so it's a pipe property
3094 // that changes over the pipe lifecycle.
3095 //
3096 // This is a self-feeding loop of madness because it ties the pipeline to GUI states
3097 // (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),
3098 // and because the pipeline is executed recursively from the end, but pipe->mask_display is set in the middle,
3099 // when it reaches the process() method of the module capturing mask preview, so you don't have this info when
3100 // planning for pipeline execution.
3101 // And you need to plan for mask preview ahead in pipe because mask preview needs to work
3102 // without using the pixelpipe cache, at least between the module requiring mask preview and gamma.c, which will actually render
3103 // the preview at the far end of the pipe.
3104 // So the not-so-clever workaround inherited from darktable was to flush all cache lines when requesting mask preview,
3105 // which flushed lines that could be reused later and were only temporarily not needed.
3106
3107 dt_iop_gui_blend_data_t *data = module->blend_data;
3108
3109 dt_dev_pixelpipe_display_mask_t new_request_mask_display = module->request_mask_display | mode;
3110
3111 // in case user requests channel display: get the cannel
3112 if(new_request_mask_display & DT_DEV_PIXELPIPE_DISPLAY_CHANNEL)
3113 {
3115
3116 if(widget == GTK_WIDGET(data->filter[1].slider))
3118
3119 new_request_mask_display &= ~DT_DEV_PIXELPIPE_DISPLAY_ANY;
3120 new_request_mask_display |= channel;
3121 }
3122
3123 // only if something has changed: reprocess center view
3124 if(new_request_mask_display != module->request_mask_display)
3125 {
3126 module->request_mask_display = new_request_mask_display;
3130 }
3131}
3132
3133// toggle channel/mask view
3135{
3136 // FIXME : there are more than 3 functions getting clever about how to setup module->request_mask_display depending on user input.
3137 // These should all use an uniform setter function.
3138 //
3139 // The lack of setter implies we don't guaranty that only 1 module can request mask display at a time.
3140 // The consequence is pipeline needs to check if module->request_mask_display AND module == dev->gui_module,
3141 // but the global pipe->mask_display is set from *_blend_process() at runtime, so it's a pipe property
3142 // that changes over the pipe lifecycle.
3143 //
3144 // This is a self-feeding loop of madness because it ties the pipeline to GUI states
3145 // (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),
3146 // and because the pipeline is executed recursively from the end, but pipe->mask_display is set in the middle,
3147 // when it reaches the process() method of the module capturing mask preview, so you don't have this info when
3148 // planning for pipeline execution.
3149 // And you need to plan for mask preview ahead in pipe because mask preview needs to work
3150 // without using the pixelpipe cache, at least between the module requiring mask preview and gamma.c, which will actually render
3151 // the preview at the far end of the pipe.
3152 // So the not-so-clever workaround inherited from darktable was to flush all cache lines when requesting mask preview,
3153 // which flushed lines that could be reused later and were only temporarily not needed.
3154
3155 dt_iop_gui_blend_data_t *data = module->blend_data;
3156
3157 dt_dev_pixelpipe_display_mask_t new_request_mask_display = module->request_mask_display & ~DT_DEV_PIXELPIPE_DISPLAY_STICKY;
3158
3159 // toggle mode
3160 if(module->request_mask_display & mode)
3161 new_request_mask_display &= ~mode;
3162 else
3163 new_request_mask_display |= mode;
3164
3166 if(new_request_mask_display & DT_DEV_PIXELPIPE_DISPLAY_STICKY)
3168 else
3169 data->save_for_leave &= ~DT_DEV_PIXELPIPE_DISPLAY_STICKY;
3171
3172 new_request_mask_display &= ~DT_DEV_PIXELPIPE_DISPLAY_ANY;
3173
3174 // in case user requests channel display: get the cannel
3175 if(new_request_mask_display & DT_DEV_PIXELPIPE_DISPLAY_CHANNEL)
3176 {
3178
3179 if(widget == GTK_WIDGET(data->filter[1].slider))
3181
3182 new_request_mask_display &= ~DT_DEV_PIXELPIPE_DISPLAY_ANY;
3183 new_request_mask_display |= channel;
3184 }
3185
3186 if(new_request_mask_display != module->request_mask_display)
3187 {
3188 module->request_mask_display = new_request_mask_display;
3192 }
3193}
3194
3204{
3205 dt_iop_gui_blend_data_t *data = !IS_NULL_PTR(module) ? module->blend_data : NULL;
3206 if(IS_NULL_PTR(data)) return;
3207
3208 const gboolean channel_active
3209 = (module->request_mask_display & DT_DEV_PIXELPIPE_DISPLAY_CHANNEL) != 0;
3210 const gboolean output_active
3211 = channel_active && (module->request_mask_display & DT_DEV_PIXELPIPE_DISPLAY_OUTPUT) != 0;
3212
3214 if(GTK_IS_TOGGLE_BUTTON(data->filter[0].channel_display))
3215 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(data->filter[0].channel_display),
3216 channel_active && !output_active);
3217 if(GTK_IS_TOGGLE_BUTTON(data->filter[1].channel_display))
3218 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(data->filter[1].channel_display),
3219 output_active);
3221}
3222
3231static void _blendop_blendif_channel_display_toggled(GtkToggleButton *button, dt_iop_module_t *module)
3232{
3233 if(dt_gui_widgets_suppressed()) return;
3234
3235 dt_iop_gui_blend_data_t *data = !IS_NULL_PTR(module) ? module->blend_data : NULL;
3236 if(IS_NULL_PTR(data) || IS_NULL_PTR(data->channel)) return;
3237
3238 const int in_out = GTK_WIDGET(button) == data->filter[1].channel_display ? 1 : 0;
3239 const gboolean active = gtk_toggle_button_get_active(button);
3240 GtkWidget *const other_button = data->filter[!in_out].channel_display;
3241
3243 if(active && GTK_IS_TOGGLE_BUTTON(other_button))
3244 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(other_button), FALSE);
3246
3247 dt_dev_pixelpipe_display_mask_t new_request_mask_display
3248 = module->request_mask_display
3249 & ~(DT_DEV_PIXELPIPE_DISPLAY_CHANNEL | DT_DEV_PIXELPIPE_DISPLAY_OUTPUT
3250 | DT_DEV_PIXELPIPE_DISPLAY_ANY | DT_DEV_PIXELPIPE_DISPLAY_STICKY);
3251
3252 if(active)
3253 {
3254 new_request_mask_display |= DT_DEV_PIXELPIPE_DISPLAY_CHANNEL
3256 | data->channel[data->tab].display_channel;
3257 if(in_out) new_request_mask_display |= DT_DEV_PIXELPIPE_DISPLAY_OUTPUT;
3258 }
3259
3261 if(active)
3263 else
3264 data->save_for_leave &= ~DT_DEV_PIXELPIPE_DISPLAY_STICKY;
3266
3267 if(new_request_mask_display == module->request_mask_display) return;
3268
3269 module->request_mask_display = new_request_mask_display;
3271
3272 dt_iop_request_focus(module);
3274}
3275
3276
3277// magic mode: if mouse cursor enters a gradient slider with shift and/or control pressed we
3278// enter channel display and/or mask display mode
3279static gboolean _blendop_blendif_enter(GtkWidget *widget, GdkEventCrossing *event, dt_iop_module_t *module)
3280{
3281 // FIXME : there are more than 3 functions getting clever about how to setup module->request_mask_display depending on user input.
3282 // These should all use an uniform setter function.
3283 //
3284 // The lack of setter implies we don't guaranty that only 1 module can request mask display at a time.
3285 // The consequence is pipeline needs to check if module->request_mask_display AND module == dev->gui_module,
3286 // but the global pipe->mask_display is set from *_blend_process() at runtime, so it's a pipe property
3287 // that changes over the pipe lifecycle.
3288 //
3289 // This is a self-feeding loop of madness because it ties the pipeline to GUI states
3290 // (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),
3291 // and because the pipeline is executed recursively from the end, but pipe->mask_display is set in the middle,
3292 // when it reaches the process() method of the module capturing mask preview, so you don't have this info when
3293 // planning for pipeline execution.
3294 // And you need to plan for mask preview ahead in pipe because mask preview needs to work
3295 // without using the pixelpipe cache, at least between the module requiring mask preview and gamma.c, which will actually render
3296 // the preview at the far end of the pipe.
3297 // So the not-so-clever workaround inherited from darktable was to flush all cache lines when requesting mask preview,
3298 // which flushed lines that could be reused later and were only temporarily not needed.
3299
3300 if(dt_gui_widgets_suppressed()) return FALSE;
3301 dt_iop_gui_blend_data_t *data = module->blend_data;
3302
3304
3305 // depending on shift modifiers we activate channel and/or mask display
3306 if(dt_modifier_is(event->state, GDK_SHIFT_MASK | GDK_CONTROL_MASK))
3307 {
3309 }
3310 else if(dt_modifier_is(event->state, GDK_SHIFT_MASK))
3311 {
3313 }
3314 else if(dt_modifier_is(event->state, GDK_CONTROL_MASK))
3315 {
3317 }
3318
3320 if(mode && data->timeout_handle)
3321 {
3322 // purge any remaining timeout handlers
3323 g_source_remove(data->timeout_handle);
3324 data->timeout_handle = 0;
3325 }
3327 {
3328 // save request_mask_display to restore later
3329 data->save_for_leave = module->request_mask_display & ~DT_DEV_PIXELPIPE_DISPLAY_STICKY;
3330 }
3332
3333 /* A plain hover only gives keyboard focus to the slider. Reapplying an
3334 * already-active CHANNEL request here would derive INPUT/OUTPUT again from
3335 * the hovered widget and switch the persistent channel toggle without a
3336 * click. Only modifier-assisted hover is allowed to change the preview. */
3338 _blendop_blendif_channel_mask_view(widget, module, mode);
3339
3340 gtk_widget_grab_focus(widget);
3341 return FALSE;
3342}
3343
3344
3345// handler for delayed mask/channel display mode switch-off
3346static gboolean _blendop_blendif_leave_delayed(gpointer data)
3347{
3348 // FIXME : there are more than 3 functions getting clever about how to setup module->request_mask_display depending on user input.
3349 // These should all use an uniform setter function.
3350 //
3351 // The lack of setter implies we don't guaranty that only 1 module can request mask display at a time.
3352 // The consequence is pipeline needs to check if module->request_mask_display AND module == dev->gui_module,
3353 // but the global pipe->mask_display is set from *_blend_process() at runtime, so it's a pipe property
3354 // that changes over the pipe lifecycle.
3355 //
3356 // This is a self-feeding loop of madness because it ties the pipeline to GUI states
3357 // (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),
3358 // and because the pipeline is executed recursively from the end, but pipe->mask_display is set in the middle,
3359 // when it reaches the process() method of the module capturing mask preview, so you don't have this info when
3360 // planning for pipeline execution.
3361 // And you need to plan for mask preview ahead in pipe because mask preview needs to work
3362 // without using the pixelpipe cache, at least between the module requiring mask preview and gamma.c, which will actually render
3363 // the preview at the far end of the pipe.
3364 // So the not-so-clever workaround inherited from darktable was to flush all cache lines when requesting mask preview,
3365 // which flushed lines that could be reused later and were only temporarily not needed.
3366
3367 dt_iop_module_t *module = (dt_iop_module_t *)data;
3368 dt_iop_gui_blend_data_t *bd = module->blend_data;
3369 int reprocess = 0;
3370
3371 dt_pthread_mutex_lock(&bd->lock);
3372 // restore saved request_mask_display and reprocess image
3373 if(bd->timeout_handle && (module->request_mask_display != (bd->save_for_leave & ~DT_DEV_PIXELPIPE_DISPLAY_STICKY)))
3374 {
3375 module->request_mask_display = bd->save_for_leave & ~DT_DEV_PIXELPIPE_DISPLAY_STICKY;
3377 reprocess = 1;
3378 }
3379 bd->timeout_handle = 0;
3380 dt_pthread_mutex_unlock(&bd->lock);
3381
3382 if(reprocess)
3383 {
3386 }
3387 // return FALSE and thereby terminate the handler
3388 return FALSE;
3389}
3390
3391// de-activate magic mode when leaving the gradient slider
3392static gboolean _blendop_blendif_leave(GtkWidget *widget, GdkEventCrossing *event, dt_iop_module_t *module)
3393{
3394 if(dt_gui_widgets_suppressed()) return FALSE;
3395 dt_iop_gui_blend_data_t *data = module->blend_data;
3396
3397 // do not immediately switch-off mask/channel display in case user leaves gradient only briefly.
3398 // instead we activate a handler function that gets triggered after some timeout
3402 data->timeout_handle = g_timeout_add(1000, _blendop_blendif_leave_delayed, module);
3404
3405 return FALSE;
3406}
3407
3408
3409static gboolean _blendop_blendif_key_press(GtkWidget *widget, GdkEventKey *event, dt_iop_module_t *module)
3410{
3411 if(dt_gui_widgets_suppressed()) return FALSE;
3412
3413 gboolean handled = FALSE;
3414
3415 switch(event->keyval)
3416 {
3417 case GDK_KEY_m:
3418 case GDK_KEY_M:
3420 handled = TRUE;
3421 }
3422
3423 if(handled)
3424 dt_iop_request_focus(module);
3425
3426 return handled;
3427}
3428
3429
3430#define COLORSTOPS(gradient) sizeof(gradient) / sizeof(dt_iop_gui_blendif_colorstop_t), gradient
3431
3433 = { { N_("L"), N_("sliders for L channel"), 1.0f / 100.0f, COLORSTOPS(_gradient_L), TRUE, 0.0f,
3436 { N_("a"), N_("sliders for a channel"), 1.0f / 256.0f, COLORSTOPS(_gradient_a), TRUE, 0.0f,
3439 { N_("b"), N_("sliders for b channel"), 1.0f / 256.0f, COLORSTOPS(_gradient_b), TRUE, 0.0f,
3442 { N_("C"), N_("sliders for chroma channel (of LCh)"), 1.0f / 100.0f, COLORSTOPS(_gradient_chroma),
3443 TRUE, 0.0f,
3446 { N_("h"), N_("sliders for hue channel (of LCh)"), 1.0f / 360.0f, COLORSTOPS(_gradient_LCh_hue),
3447 FALSE, 0.0f,
3449 _blendif_scale_print_hue, NULL, N_("hue") },
3450 { NULL } };
3451
3453 = { { N_("g"), N_("sliders for gray value"), 1.0f / 255.0f, COLORSTOPS(_gradient_gray), TRUE, 0.0f,
3456 { N_("R"), N_("sliders for red channel"), 1.0f / 255.0f, COLORSTOPS(_gradient_red), TRUE, 0.0f,
3459 { N_("G"), N_("sliders for green channel"), 1.0f / 255.0f, COLORSTOPS(_gradient_green), TRUE, 0.0f,
3462 { N_("B"), N_("sliders for blue channel"), 1.0f / 255.0f, COLORSTOPS(_gradient_blue), TRUE, 0.0f,
3465 { N_("H"), N_("sliders for hue channel (of HSL)"), 1.0f / 360.0f, COLORSTOPS(_gradient_HSL_hue),
3466 FALSE, 0.0f,
3468 _blendif_scale_print_hue, NULL, N_("hue") },
3469 { N_("S"), N_("sliders for chroma channel (of HSL)"), 1.0f / 100.0f, COLORSTOPS(_gradient_chroma),
3470 FALSE, 0.0f,
3473 { N_("L"), N_("sliders for value channel (of HSL)"), 1.0f / 100.0f, COLORSTOPS(_gradient_gray),
3474 FALSE, 0.0f,
3477 { NULL } };
3478
3480 = { { N_("g"), N_("sliders for gray value"), 1.0f / 255.0f, COLORSTOPS(_gradient_gray), TRUE, 0.0f,
3483 { N_("R"), N_("sliders for red channel"), 1.0f / 255.0f, COLORSTOPS(_gradient_red), TRUE, 0.0f,
3486 { N_("G"), N_("sliders for green channel"), 1.0f / 255.0f, COLORSTOPS(_gradient_green), TRUE, 0.0f,
3489 { N_("B"), N_("sliders for blue channel"), 1.0f / 255.0f, COLORSTOPS(_gradient_blue), TRUE, 0.0f,
3492 { N_("Jz"), N_("sliders for value channel (of JzCzhz)"), 1.0f / 100.0f, COLORSTOPS(_gradient_gray),
3493 TRUE, -6.64385619f, // cf. _blend_init_blendif_boost_parameters
3496 { N_("Cz"), N_("sliders for chroma channel (of JzCzhz)"), 1.0f / 100.0f, COLORSTOPS(_gradient_chroma),
3497 TRUE, -6.64385619f, // cf. _blend_init_blendif_boost_parameters
3500 { N_("hz"), N_("sliders for hue channel (of JzCzhz)"), 1.0f / 360.0f, COLORSTOPS(_gradient_JzCzhz_hue),
3501 FALSE, 0.0f,
3503 _blendif_scale_print_hue, NULL, N_("hue") },
3504 { NULL } };
3505
3506const char *slider_tooltip[] = { N_("adjustment based on input received by this module:\n* range defined by upper markers: "
3507 "blend fully\n* range defined by lower markers: do not blend at all\n* range between "
3508 "adjacent upper/lower markers: blend gradually"),
3509 N_("adjustment based on unblended output of this module:\n* range defined by upper "
3510 "markers: blend fully\n* range defined by lower markers: do not blend at all\n* range "
3511 "between adjacent upper/lower markers: blend gradually") };
3512
3514{
3515 // FIXME : there are more than 3 functions getting clever about how to setup module->request_mask_display depending on user input.
3516 // These should all use an uniform setter function.
3517 //
3518 // The lack of setter implies we don't guaranty that only 1 module can request mask display at a time.
3519 // The consequence is pipeline needs to check if module->request_mask_display AND module == dev->gui_module,
3520 // but the global pipe->mask_display is set from *_blend_process() at runtime, so it's a pipe property
3521 // that changes over the pipe lifecycle.
3522 //
3523 // This is a self-feeding loop of madness because it ties the pipeline to GUI states
3524 // (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),
3525 // and because the pipeline is executed recursively from the end, but pipe->mask_display is set in the middle,
3526 // when it reaches the process() method of the module capturing mask preview, so you don't have this info when
3527 // planning for pipeline execution.
3528 // And you need to plan for mask preview ahead in pipe because mask preview needs to work
3529 // without using the pixelpipe cache, at least between the module requiring mask preview and gamma.c, which will actually render
3530 // the preview at the far end of the pipe.
3531 // So the not-so-clever workaround inherited from darktable was to flush all cache lines when requesting mask preview,
3532 // which flushed lines that could be reused later and were only temporarily not needed.
3533 dt_iop_gui_blend_data_t *bd = module->blend_data;
3534
3535 if(IS_NULL_PTR(bd) || !bd->blendif_support || !bd->blendif_inited) return;
3536
3538
3540 if(bd->timeout_handle)
3541 {
3542 g_source_remove(bd->timeout_handle);
3543 bd->timeout_handle = 0;
3545 {
3546 module->request_mask_display = bd->save_for_leave & ~DT_DEV_PIXELPIPE_DISPLAY_STICKY;
3549 }
3550 }
3552
3553 /* update output channel mask visibility */
3554 gtk_widget_set_visible(GTK_WIDGET(bd->filter[1].box), bd->output_channels_shown);
3555
3556 /* update tabs */
3557 if(bd->channel_tabs_csp != bd->csp)
3558 {
3559 bd->channel = NULL;
3560
3561 switch(bd->csp)
3562 {
3564 bd->channel = Lab_channels;
3565 break;
3567 bd->channel = rgb_channels;
3568 break;
3570 bd->channel = rgbj_channels;
3571 break;
3572 default:
3573 assert(FALSE); // blendif not supported for RAW, which is already caught upstream; we should not get
3574 // here
3575 }
3576
3578
3579 /* remove tabs before adding others */
3581
3582 bd->channel_tabs_csp = bd->csp;
3583
3584 int index = 0;
3585 for(const dt_iop_gui_blendif_channel_t *ch = bd->channel; !IS_NULL_PTR(ch->label); ch++, index++)
3586 {
3587 dt_ui_notebook_page(bd->channel_tabs, ch->label, _(ch->tooltip));
3588 gtk_widget_show_all(GTK_WIDGET(gtk_notebook_get_nth_page(bd->channel_tabs, index)));
3589 }
3590
3591 bd->tab = 0;
3592 gtk_notebook_set_current_page(GTK_NOTEBOOK(bd->channel_tabs), bd->tab);
3593 }
3594
3595 _blendop_blendif_update_tab(module, bd->tab);
3597
3599}
3600
3601
3602void dt_iop_gui_init_blendif(GtkBox *blendw, dt_iop_module_t *module, GtkWidget *blendif_header)
3603{
3605
3606 bd->blendif_box = blendw;
3607
3608 /* create and add blendif support if module supports it */
3609 if(bd->blendif_support)
3610 {
3611 dt_iop_togglebutton_new_no_register(module, "blend`tools", N_("reset blend mask settings"), NULL,
3612 G_CALLBACK(_blendop_blendif_reset), FALSE, 0, 0,
3613 dtgtk_cairo_paint_reset, blendif_header);
3614
3615 GtkWidget *header = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, DT_GUI_BOX_SPACING);
3616 gtk_widget_set_name(header, "blendif-pickers");
3617
3618 bd->tab = 0;
3620 bd->channel_tabs = GTK_NOTEBOOK(gtk_notebook_new());
3621 gtk_notebook_set_scrollable(bd->channel_tabs, TRUE);
3622 gtk_box_pack_start(GTK_BOX(bd->blendif_box), GTK_WIDGET(bd->channel_tabs), FALSE, FALSE, 0);
3623 gtk_notebook_set_action_widget(GTK_NOTEBOOK(bd->channel_tabs), header, GTK_PACK_END);
3624 dt_gui_add_class(GTK_WIDGET(bd->channel_tabs), "empty");
3625
3627 gtk_widget_set_tooltip_text(bd->colorpicker, _("pick GUI color from image\nctrl+click or right-click to select an area"));
3628
3632
3633 gtk_widget_set_tooltip_text(bd->colorpicker_set_values, _("set the range based on an area from the image\n"
3634 "drag to use the input image\n"
3635 "ctrl+drag to use the output image"));
3636
3637 dt_iop_togglebutton_new_no_register(module, "blend`tools", N_("invert all channel's polarities"), NULL,
3638 G_CALLBACK(_blendop_blendif_invert), FALSE, 0, 0,
3639 dtgtk_cairo_paint_invert, header);
3640
3641 gtk_widget_show_all(header);
3642
3643 for(int in_out = 1; in_out >= 0; in_out--)
3644 {
3645 dt_iop_gui_blendif_filter_t *sl = &bd->filter[in_out];
3646
3647 GtkWidget *slider_box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, DT_GUI_BOX_SPACING);
3648
3650 in_out ? "blend-upper" : "blend-lower"));
3651 gtk_box_pack_start(GTK_BOX(slider_box), GTK_WIDGET(sl->slider), TRUE, TRUE, 0);
3652
3654 dt_gui_add_class(sl->polarity, "dt_ignore_fg_state");
3655
3656 gtk_widget_set_tooltip_text(sl->polarity, _("toggle polarity. best seen by enabling 'display mask'"));
3657 gtk_box_pack_end(GTK_BOX(slider_box), GTK_WIDGET(sl->polarity), FALSE, FALSE, 0);
3658
3659 GtkWidget *label_box = gtk_grid_new();
3660 gtk_grid_set_column_homogeneous(GTK_GRID(label_box), TRUE);
3661
3662 sl->head = GTK_LABEL(dt_ui_label_new(in_out ? _("output") : _("input")));
3663 gtk_grid_attach(GTK_GRID(label_box), GTK_WIDGET(sl->head), 0, 0, 1, 1);
3664
3665 GtkWidget *overlay = gtk_overlay_new();
3666 gtk_grid_attach(GTK_GRID(label_box), overlay, 1, 0, 3, 1);
3667
3668 sl->picker_label = GTK_LABEL(gtk_label_new(""));
3669 gtk_widget_set_name(GTK_WIDGET(sl->picker_label), "blend-data");
3670 gtk_label_set_xalign(sl->picker_label, .0);
3671 gtk_label_set_yalign(sl->picker_label, 1.0);
3672 gtk_container_add(GTK_CONTAINER(overlay), GTK_WIDGET(sl->picker_label));
3673
3674 for(int k = 0; k < 4; k++)
3675 {
3676 sl->label[k] = GTK_LABEL(gtk_label_new(NULL));
3677 gtk_widget_set_name(GTK_WIDGET(sl->label[k]), "blend-data");
3678 gtk_label_set_xalign(sl->label[k], .35 + k * .65/3);
3679 gtk_label_set_yalign(sl->label[k], k % 2);
3680 gtk_overlay_add_overlay(GTK_OVERLAY(overlay), GTK_WIDGET(sl->label[k]));
3681 }
3682
3683 gtk_widget_set_tooltip_text(GTK_WIDGET(sl->slider),
3684 _("double-click to reset.\npress 'm' to toggle mask view."));
3685 gtk_widget_set_tooltip_text(GTK_WIDGET(sl->head), _(slider_tooltip[in_out]));
3686
3687 g_signal_connect(G_OBJECT(sl->slider), "value-changed", G_CALLBACK(_blendop_blendif_sliders_callback), bd);
3688 g_signal_connect(G_OBJECT(sl->slider), "value-reset", G_CALLBACK(_blendop_blendif_sliders_reset_callback), bd);
3689 g_signal_connect(G_OBJECT(sl->slider), "leave-notify-event", G_CALLBACK(_blendop_blendif_leave), module);
3690 g_signal_connect(G_OBJECT(sl->slider), "enter-notify-event", G_CALLBACK(_blendop_blendif_enter), module);
3691 g_signal_connect(G_OBJECT(sl->slider), "key-press-event", G_CALLBACK(_blendop_blendif_key_press), module);
3692 g_signal_connect(G_OBJECT(sl->polarity), "toggled", G_CALLBACK(_blendop_blendif_polarity_callback), bd);
3693
3694 // The label is semantically "part of" the slider, even though it's a different widget,
3695 // so the parent box has no internal spacing between children to keep them grouped.
3696 sl->box = GTK_BOX(gtk_box_new(GTK_ORIENTATION_VERTICAL, DT_GUI_BOX_SPACING));
3697 GtkWidget *channel_box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
3698 {
3699 gtk_box_pack_start(GTK_BOX(channel_box), GTK_WIDGET(label_box), FALSE, FALSE, 0);
3700 gtk_box_pack_start(GTK_BOX(channel_box), GTK_WIDGET(slider_box), FALSE, FALSE, 0);
3701 }
3702 gtk_box_pack_start(GTK_BOX(sl->box), channel_box, FALSE, FALSE, 0);
3703
3704 GtkWidget *display_controls = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, DT_GUI_BOX_SPACING);
3705 {
3706 sl->log_scale = gtk_toggle_button_new_with_label(_("Log scale"));
3707 gtk_widget_set_tooltip_text(sl->log_scale,
3708 _("toggle the alternative logarithmic or magnified scale for this channel"));
3709 g_signal_connect(G_OBJECT(sl->log_scale), "toggled",
3710 G_CALLBACK(_blendop_blendif_log_scale_toggled), module);
3711 gtk_box_pack_start(GTK_BOX(display_controls), sl->log_scale, FALSE, FALSE, 0);
3712
3714 gtk_widget_set_tooltip_text(
3715 sl->channel_display,
3716 in_out ? _("display the current channel from the unblended module output")
3717 : _("display the current channel from the module input"));
3718 g_signal_connect(G_OBJECT(sl->channel_display), "toggled",
3719 G_CALLBACK(_blendop_blendif_channel_display_toggled), module);
3720 gtk_box_pack_start(GTK_BOX(display_controls), sl->channel_display, FALSE, FALSE, 0);
3721 }
3722 gtk_box_pack_start(GTK_BOX(sl->box), display_controls, FALSE, FALSE, 0);
3723 gtk_box_pack_start(GTK_BOX(bd->blendif_box), GTK_WIDGET(sl->box), FALSE, FALSE, 0);
3724
3725 }
3726
3733 gtk_widget_set_tooltip_text(bd->channel_boost_factor_slider, _("adjust the boost factor of the channel mask"));
3734 gtk_widget_set_sensitive(bd->channel_boost_factor_slider, FALSE);
3735
3736 g_signal_connect(G_OBJECT(bd->channel_boost_factor_slider), "value-changed",
3738
3739 gtk_box_pack_start(GTK_BOX(bd->blendif_box), GTK_WIDGET(bd->channel_boost_factor_slider), FALSE, FALSE, 0);
3740
3741 g_signal_connect(G_OBJECT(bd->channel_tabs), "switch_page", G_CALLBACK(_blendop_blendif_tab_switch), bd);
3742 g_signal_connect(G_OBJECT(bd->colorpicker), "toggled", G_CALLBACK(_update_gradient_slider_pickers), module);
3743 g_signal_connect(G_OBJECT(bd->colorpicker_set_values), "toggled", G_CALLBACK(_update_gradient_slider_pickers), module);
3744
3745 bd->blendif_inited = 1;
3746 }
3747 else
3748 {
3749 gchar *module_name = dt_history_item_get_name(module);
3750 gchar *markup = g_markup_printf_escaped(
3751 _("<i>Parametric masking is disabled because the <b>%s</b> module does not provide compatible color channels.</i>"),
3752 module_name);
3753 GtkWidget *label = gtk_label_new(NULL);
3754 gtk_label_set_markup(GTK_LABEL(label), markup);
3755 gtk_label_set_xalign(GTK_LABEL(label), 0.0f);
3756 gtk_label_set_line_wrap(GTK_LABEL(label), TRUE);
3757 gtk_box_pack_start(GTK_BOX(bd->blendif_box), label, FALSE, FALSE, 0);
3758 dt_free(markup);
3759 dt_free(module_name);
3760 }
3761}
3762
3763void dt_iop_gui_init_contours(GtkBox *blendw, dt_iop_module_t *module)
3764{
3766
3767 gtk_box_pack_start(blendw, bd->details_slider, FALSE, FALSE, 0);
3768 gtk_box_pack_start(blendw, bd->blur_radius_slider, FALSE, FALSE, 0);
3769 gtk_box_pack_start(blendw, bd->masks_feathering_guide_combo, FALSE, FALSE, 0);
3770 gtk_box_pack_start(blendw, bd->feathering_radius_slider, FALSE, FALSE, 0);
3771 gtk_box_pack_start(blendw, bd->brightness_slider, FALSE, FALSE, 0);
3772 gtk_box_pack_start(blendw, bd->contrast_slider, FALSE, FALSE, 0);
3773}
3774
3776{
3778 dt_develop_blend_params_t *bp = module->blend_params;
3779
3780 if(IS_NULL_PTR(bd) || !bd->masks_support || !bd->masks_inited) return;
3781
3783
3784 /* update masks state */
3785 dt_masks_form_gui_t *form_gui = module->dev->form_gui;
3786 dt_masks_form_t *visible_form = dt_masks_get_visible_form(module->dev);
3787 const gboolean module_creation = !IS_NULL_PTR(form_gui) && form_gui->creation
3788 && form_gui->creation_module == module;
3789 const dt_masks_type_t creation_type = module_creation && !IS_NULL_PTR(visible_form)
3790 ? visible_form->type
3791 : (module_creation ? form_gui->creation_type : DT_MASKS_NONE);
3793 const gboolean has_group_shapes = grp && (grp->type & DT_MASKS_GROUP) && grp->points;
3794 if(GTK_IS_WIDGET(bd->masks_combo))
3795 {
3797 if(has_group_shapes)
3798 {
3799 char txt[512];
3800 const guint n = g_list_length(grp->points);
3801 snprintf(txt, sizeof(txt), ngettext("%d shape used", "%d shapes used", n), n);
3803 }
3804 else
3805 {
3806 dt_bauhaus_combobox_add(bd->masks_combo, _("no mask used"));
3807 }
3809 gtk_widget_queue_draw(bd->masks_combo);
3810 }
3811
3812 if(!has_group_shapes)
3813 {
3815 // reset the gui
3817 }
3818
3819 if(bd->masks_support)
3820 {
3821 if(GTK_IS_TOGGLE_BUTTON(bd->masks_edit))
3822 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(bd->masks_edit),
3823 !module_creation && bd->masks_shown != DT_MASKS_EDIT_OFF);
3824
3825 if(GTK_IS_TOGGLE_BUTTON(bd->masks_polarity))
3826 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(bd->masks_polarity),
3828 }
3829
3830 // update buttons status
3831 for(int n = 0; n < DEVELOP_MASKS_NB_SHAPES; n++)
3832 {
3833 if(!GTK_IS_TOGGLE_BUTTON(bd->masks_shapes[n])) continue;
3834
3835 if(module_creation && (creation_type & bd->masks_type[n]))
3836 {
3837 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(bd->masks_shapes[n]), TRUE);
3838 }
3839 else
3840 {
3841 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(bd->masks_shapes[n]), FALSE);
3842 }
3843 }
3844
3847
3849}
3850
3851void dt_iop_gui_init_masks(GtkBox *blendw, dt_iop_module_t *module)
3852{
3854
3855 bd->masks_box = blendw;
3856
3857 /* create and add masks support if module supports it */
3858 if(bd->masks_support)
3859 {
3860 bd->masks_combo_ids = NULL;
3862
3863 bd->lists_box = gtk_box_new(GTK_ORIENTATION_VERTICAL, DT_GUI_BOX_SPACING);
3864
3865 GtkCellRenderer *renderer = NULL;
3866 GtkTreeSelection *selection = NULL;
3867
3868 GtkWidget *group_shapes_header = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, DT_GUI_BOX_SPACING);
3869 bd->group_shapes_label = gtk_entry_new();
3870 gchar *group_placeholder = dt_dev_get_masks_group_name(module);
3871 gtk_entry_set_placeholder_text(GTK_ENTRY(bd->group_shapes_label), group_placeholder);
3872 g_free(group_placeholder);
3873 gtk_widget_set_tooltip_text(bd->group_shapes_label, _("Edit current module mask name"));
3874 gtk_widget_set_halign(bd->group_shapes_label, GTK_ALIGN_FILL);
3875 gtk_widget_set_hexpand(bd->group_shapes_label, TRUE);
3876 g_signal_connect(bd->group_shapes_label, "activate", G_CALLBACK(_blendop_masks_group_name_activate), module);
3877 g_signal_connect(bd->group_shapes_label, "focus-out-event",
3878 G_CALLBACK(_blendop_masks_group_name_focus_out), module);
3879 gtk_box_pack_start(GTK_BOX(group_shapes_header), bd->group_shapes_label, TRUE, TRUE, 0);
3880
3881 // buttons for mask polarity and edit mode
3882 bd->masks_polarity = dt_iop_togglebutton_new_no_register(module, "blend`tools", N_("toggle polarity of drawn mask"), NULL,
3884 FALSE, 0, 0, dtgtk_cairo_paint_invert, group_shapes_header);
3886 dt_gui_add_class(bd->masks_polarity, "dt_ignore_fg_state");
3887
3888 bd->masks_edit = dt_iop_togglebutton_new_no_register(module, "blend`tools", N_("show and edit mask elements"),
3889 N_("show and edit in restricted mode"),
3890 G_CALLBACK(_blendop_masks_show_and_edit),
3891 FALSE, 0, 0, dtgtk_cairo_paint_masks_edit, group_shapes_header);
3892
3893 gtk_box_pack_start(GTK_BOX(bd->lists_box), group_shapes_header, FALSE, FALSE, 0);
3894
3895 // Current mask list
3896 bd->masks_group_treeview = gtk_tree_view_new();
3897 bd->group_shapes_store = gtk_tree_store_new(BLENDOP_MASKS_GROUP_COL_COUNT, GDK_TYPE_PIXBUF,
3898 GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_INT,
3899 G_TYPE_INT, G_TYPE_INT, G_TYPE_INT);
3900 gtk_tree_view_set_model(GTK_TREE_VIEW(bd->masks_group_treeview), GTK_TREE_MODEL(bd->group_shapes_store));
3901
3902 // Initialize mask operation icons for treeview display
3904
3905 bd->group_shapes_col = gtk_tree_view_column_new();
3906 gtk_tree_view_append_column(GTK_TREE_VIEW(bd->masks_group_treeview), bd->group_shapes_col);
3907
3908 renderer = gtk_cell_renderer_pixbuf_new();
3909 gtk_tree_view_column_pack_start(bd->group_shapes_col, renderer, FALSE);
3910 gtk_tree_view_column_add_attribute(bd->group_shapes_col, renderer, "pixbuf", BLENDOP_MASKS_GROUP_COL_OP_ICON);
3911
3912 renderer = gtk_cell_renderer_pixbuf_new();
3913 gtk_tree_view_column_pack_start(bd->group_shapes_col, renderer, FALSE);
3914 gtk_tree_view_column_add_attribute(bd->group_shapes_col, renderer, "pixbuf", BLENDOP_MASKS_GROUP_COL_INV_ICON);
3915
3916 renderer = gtk_cell_renderer_text_new();
3917 gtk_tree_view_column_pack_start(bd->group_shapes_col, renderer, TRUE);
3918 gtk_tree_view_column_add_attribute(bd->group_shapes_col, renderer, "text", BLENDOP_MASKS_GROUP_COL_NAME);
3919
3920 // Per-row action icons: unlink (detach from this mask) and delete (remove altogether).
3921 // Clicks are handled in _blendop_masks_group_button_pressed by matching the column.
3922 bd->group_unlink_col = gtk_tree_view_column_new();
3923 renderer = gtk_cell_renderer_pixbuf_new();
3924 g_object_set(renderer, "icon-name", "list-remove-symbolic", "stock-size", GTK_ICON_SIZE_MENU, NULL);
3925 gtk_tree_view_column_pack_start(bd->group_unlink_col, renderer, FALSE);
3926 gtk_tree_view_column_set_sizing(bd->group_unlink_col, GTK_TREE_VIEW_COLUMN_FIXED);
3927 gtk_tree_view_column_set_fixed_width(bd->group_unlink_col, DT_PIXEL_APPLY_DPI(24));
3928 gtk_tree_view_append_column(GTK_TREE_VIEW(bd->masks_group_treeview), bd->group_unlink_col);
3929
3930 bd->group_delete_col = gtk_tree_view_column_new();
3931 renderer = gtk_cell_renderer_pixbuf_new();
3932 g_object_set(renderer, "icon-name", "user-trash-symbolic", "stock-size", GTK_ICON_SIZE_MENU, NULL);
3933 gtk_tree_view_column_pack_start(bd->group_delete_col, renderer, FALSE);
3934 gtk_tree_view_column_set_sizing(bd->group_delete_col, GTK_TREE_VIEW_COLUMN_FIXED);
3935 gtk_tree_view_column_set_fixed_width(bd->group_delete_col, DT_PIXEL_APPLY_DPI(24));
3936 gtk_tree_view_append_column(GTK_TREE_VIEW(bd->masks_group_treeview), bd->group_delete_col);
3937
3938 // Keep the name column expanding so the action icons stay flush right.
3939 gtk_tree_view_column_set_expand(bd->group_shapes_col, TRUE);
3940
3941 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(bd->masks_group_treeview));
3942 gtk_tree_selection_set_mode(selection, GTK_SELECTION_SINGLE);
3943 g_signal_connect(selection, "changed", G_CALLBACK(_blendop_masks_group_selection_changed), module);
3944 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(bd->masks_group_treeview), FALSE);
3945 gtk_tree_view_set_show_expanders(GTK_TREE_VIEW(bd->masks_group_treeview), TRUE);
3946 g_signal_connect(bd->masks_group_treeview, "button-press-event",
3947 G_CALLBACK(_blendop_masks_group_button_pressed), module);
3948
3949 gtk_widget_set_has_tooltip(bd->masks_group_treeview, TRUE);
3950 g_signal_connect(bd->masks_group_treeview, "query-tooltip",
3951 G_CALLBACK(_blendop_masks_group_query_tooltip), module);
3952
3954 "plugins/darkroom/masks/group_list_height", DT_UI_RESIZE_DYNAMIC);
3955 gtk_widget_set_vexpand(bd->group_shapes_sw, TRUE);
3956
3957 // Creating shapes buttons (circle, ellipse ....)
3959 if(!GTK_IS_WIDGET(bd->all_shapes_buttons)) return;
3960
3961 // Wire shapes toggle button
3962 bd->wire_shape_toggle = gtk_toggle_button_new_with_label(_("Attach shapes"));
3963 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."));
3964
3965 GtkWidget *bottom_bar = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, DT_GUI_BOX_SPACING);
3966 gtk_box_pack_start(GTK_BOX(bottom_bar), bd->wire_shape_toggle, FALSE, FALSE, 0);
3967 gtk_box_pack_end(GTK_BOX(bottom_bar), bd->all_shapes_buttons, FALSE, FALSE, 0);
3968 gtk_box_pack_end(GTK_BOX(bd->lists_box), bottom_bar, FALSE, FALSE, 0);
3969
3970
3971 // All shapes list
3972 bd->masks_treeview = gtk_tree_view_new();
3973 bd->all_shapes_store = gtk_list_store_new(BLENDOP_MASKS_ALL_COL_COUNT, G_TYPE_BOOLEAN,
3974 G_TYPE_STRING, G_TYPE_INT, G_TYPE_BOOLEAN,
3975 G_TYPE_STRING, G_TYPE_STRING);
3976 gtk_tree_view_set_model(GTK_TREE_VIEW(bd->masks_treeview), GTK_TREE_MODEL(bd->all_shapes_store));
3977
3978 bd->all_shapes_col = gtk_tree_view_column_new();
3979 gtk_tree_view_append_column(GTK_TREE_VIEW(bd->masks_treeview), bd->all_shapes_col);
3980
3981 renderer = gtk_cell_renderer_toggle_new();
3982 g_object_set(renderer, "activatable", TRUE, NULL);
3983 g_signal_connect(renderer, "toggled", G_CALLBACK(_blendop_masks_all_toggled), module);
3984 gtk_tree_view_column_pack_start(bd->all_shapes_col, renderer, FALSE);
3985 gtk_tree_view_column_add_attribute(bd->all_shapes_col, renderer, "active", BLENDOP_MASKS_ALL_COL_ACTIVE);
3986 gtk_tree_view_column_add_attribute(bd->all_shapes_col, renderer, "sensitive", BLENDOP_MASKS_ALL_COL_SENSITIVE);
3987
3988 GtkTreeViewColumn *all_shapes_name_col = gtk_tree_view_column_new();
3989 gtk_tree_view_append_column(GTK_TREE_VIEW(bd->masks_treeview), all_shapes_name_col);
3990
3991 renderer = gtk_cell_renderer_text_new();
3992 g_object_set(renderer, "editable", TRUE, NULL);
3993 g_signal_connect(renderer, "edited", G_CALLBACK(_blendop_masks_all_name_edited), module);
3994 gtk_tree_view_column_pack_start(all_shapes_name_col, renderer, TRUE);
3995 gtk_tree_view_column_add_attribute(all_shapes_name_col, renderer, "markup", BLENDOP_MASKS_ALL_COL_MARKUP);
3996 gtk_tree_view_column_add_attribute(all_shapes_name_col, renderer, "sensitive", BLENDOP_MASKS_ALL_COL_SENSITIVE);
3997 g_object_set_data(G_OBJECT(bd->masks_treeview), "blendop-masks-name-renderer", renderer);
3998
3999 GtkTreeViewColumn *all_shapes_status_col = gtk_tree_view_column_new();
4000 gtk_tree_view_append_column(GTK_TREE_VIEW(bd->masks_treeview), all_shapes_status_col);
4001
4002 renderer = gtk_cell_renderer_text_new();
4003 g_object_set(renderer, "xalign", 1.0f, NULL);
4004 gtk_tree_view_column_pack_end(all_shapes_status_col, renderer, TRUE);
4005 gtk_tree_view_column_add_attribute(all_shapes_status_col, renderer, "markup",
4007 gtk_tree_view_column_add_attribute(all_shapes_status_col, renderer, "sensitive",
4009
4010 // Per-row delete icon, mirroring group_delete_col above. Clicks are handled in
4011 // _blendop_masks_all_button_pressed by matching the column.
4012 bd->all_shapes_delete_col = gtk_tree_view_column_new();
4013 renderer = gtk_cell_renderer_pixbuf_new();
4014 g_object_set(renderer, "icon-name", "user-trash-symbolic", "stock-size", GTK_ICON_SIZE_MENU, NULL);
4015 gtk_tree_view_column_pack_start(bd->all_shapes_delete_col, renderer, FALSE);
4016 gtk_tree_view_column_set_sizing(bd->all_shapes_delete_col, GTK_TREE_VIEW_COLUMN_FIXED);
4017 gtk_tree_view_column_set_fixed_width(bd->all_shapes_delete_col, DT_PIXEL_APPLY_DPI(24));
4018 gtk_tree_view_append_column(GTK_TREE_VIEW(bd->masks_treeview), bd->all_shapes_delete_col);
4019
4020 // Keep the name column expanding so the status text and trash icon stay flush right,
4021 // same as group_shapes_col above.
4022 gtk_tree_view_column_set_expand(all_shapes_name_col, TRUE);
4023
4024 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(bd->masks_treeview));
4025 gtk_tree_selection_set_mode(selection, GTK_SELECTION_SINGLE);
4026 g_signal_connect(selection, "changed", G_CALLBACK(_blendop_masks_all_selection_changed), module);
4027 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(bd->masks_treeview), FALSE);
4028 g_signal_connect(bd->masks_treeview, "button-press-event",
4029 G_CALLBACK(_blendop_masks_all_button_pressed), module);
4030
4032 "plugins/darkroom/masks/all_list_height", DT_UI_RESIZE_DYNAMIC);
4033 gtk_widget_set_vexpand(bd->all_shapes_sw, TRUE);
4034
4035 // The two lists share the same slot; Edit selects which one is visible.
4036 bd->lists_stack = gtk_stack_new();
4037 gtk_stack_set_transition_type(GTK_STACK(bd->lists_stack), GTK_STACK_TRANSITION_TYPE_NONE);
4038 gtk_stack_set_homogeneous(GTK_STACK(bd->lists_stack), FALSE);
4039 gtk_stack_add_named(GTK_STACK(bd->lists_stack), bd->group_shapes_sw, "group");
4040 gtk_stack_add_named(GTK_STACK(bd->lists_stack), bd->all_shapes_sw, "all");
4041 gtk_box_pack_start(GTK_BOX(bd->lists_box), bd->lists_stack, TRUE, TRUE, 0);
4042
4043 // Default state:
4044 // - current-module tree is visible
4045 // - all-shapes tree is hidden
4046 // The toggle callback flips these two scrolled windows.
4047 g_signal_connect(bd->wire_shape_toggle, "toggled", G_CALLBACK(_blendop_masks_edit_list_toggle), module);
4048
4049 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(bd->wire_shape_toggle), FALSE);
4050 _blendop_masks_edit_list_toggle(GTK_TOGGLE_BUTTON(bd->wire_shape_toggle), module);
4051
4052 gtk_box_pack_start(GTK_BOX(bd->masks_box), bd->lists_box, TRUE, TRUE, 0);
4053
4055 (&bd->masks_cs,
4056 "plugins/darkroom/masks/wacom",
4057 _("Brush options"),
4058 GTK_BOX(bd->masks_box), GTK_PACK_END);
4059
4060 GtkWidget *brush_smoothing = dt_bauhaus_combobox_from_conf(darktable.bauhaus, DT_GUI_MODULE(NULL), "brush_smoothing");
4061 gtk_box_pack_start(GTK_BOX(bd->masks_cs.container), brush_smoothing, TRUE, TRUE, 0);
4062
4063 GtkWidget *pressure_mapping = dt_bauhaus_combobox_from_conf(darktable.bauhaus, DT_GUI_MODULE(NULL), "pressure_sensitivity");
4064 gtk_box_pack_start(GTK_BOX(bd->masks_cs.container), pressure_mapping, TRUE, TRUE, 0);
4065
4066 bd->masks_inited = 1;
4068 }
4069 else
4070 {
4071 gchar *module_name = dt_history_item_get_name(module);
4072 gchar *markup = g_markup_printf_escaped(
4073 _("<i>Drawn masking is disabled because the <b>%s</b> module manages drawn shapes internally.</i>"),
4074 module_name);
4075 GtkWidget *label = gtk_label_new(NULL);
4076 gtk_label_set_markup(GTK_LABEL(label), markup);
4077 gtk_label_set_xalign(GTK_LABEL(label), 0.0f);
4078 gtk_label_set_line_wrap(GTK_LABEL(label), TRUE);
4079 gtk_box_pack_start(GTK_BOX(bd->masks_box), label, FALSE, FALSE, 0);
4080 dt_free(markup);
4081 dt_free(module_name);
4082 }
4083}
4084
4090
4091static void _raster_combo_populate(GtkWidget *w, void *m)
4092{
4093 dt_iop_module_t *module = (dt_iop_module_t *)m;
4094 dt_iop_request_focus(module);
4095
4097
4098 raster_combo_entry_t *entry = (raster_combo_entry_t *)calloc(1, sizeof(raster_combo_entry_t));
4099 if(IS_NULL_PTR(entry)) return;
4100 dt_bauhaus_combobox_add_full(w, _("no mask used"), DT_BAUHAUS_COMBOBOX_ALIGN_RIGHT, entry, free, TRUE);
4101
4102 int i = 1;
4103
4104 for(GList* iter = darktable.develop->iop; iter; iter = g_list_next(iter))
4105 {
4106 dt_iop_module_t *iop = (dt_iop_module_t *)iter->data;
4107 if(iop == module)
4108 break;
4109
4110 GHashTableIter masks_iter;
4111 gpointer key, value;
4112
4113 g_hash_table_iter_init(&masks_iter, iop->raster_mask.source.masks);
4114 while(g_hash_table_iter_next(&masks_iter, &key, &value))
4115 {
4116 const int id = GPOINTER_TO_INT(key);
4117 const char *modulename = (char *)value;
4118 entry = (raster_combo_entry_t *)malloc(sizeof(raster_combo_entry_t));
4119 entry->module = iop;
4120 entry->id = id;
4122 if(iop == module->raster_mask.sink.source && module->raster_mask.sink.id == id)
4124 i++;
4125 }
4126 }
4127}
4128
4130{
4132
4133 // nothing to do
4134 if(entry->module == module->raster_mask.sink.source && entry->id == module->raster_mask.sink.id)
4135 return;
4136
4137 if(module->raster_mask.sink.source)
4138 {
4139 // we no longer use this one
4140 g_hash_table_remove(module->raster_mask.sink.source->raster_mask.source.users, module);
4141 }
4142
4143 module->raster_mask.sink.source = entry->module;
4144 module->raster_mask.sink.id = entry->id;
4145
4146 if(entry->module)
4147 {
4148 // The value is the provider-local mask id. dt_iop_is_raster_mask_used()
4149 // reads it to decide which side-band mask buffers the pixelpipe must
4150 // publish. g_hash_table_add() stores the consumer pointer as both key and
4151 // value, making an existing mask visible in the GUI but impossible to
4152 // retrieve reliably from the pipeline.
4153 g_hash_table_insert(entry->module->raster_mask.source.users, module,
4154 GINT_TO_POINTER(entry->id));
4155
4156 // update blend_params!
4157 memcpy(module->blend_params->raster_mask_source, entry->module->op, sizeof(module->blend_params->raster_mask_source));
4158 module->blend_params->raster_mask_instance = entry->module->multi_priority;
4159 module->blend_params->raster_mask_id = entry->id;
4160 }
4161 else
4162 {
4163 memset(module->blend_params->raster_mask_source, 0, sizeof(module->blend_params->raster_mask_source));
4164 module->blend_params->raster_mask_instance = 0;
4165 module->blend_params->raster_mask_id = 0;
4166 }
4167
4168 dt_dev_add_history_item(module->dev, module, TRUE, TRUE);
4170}
4171
4173{
4175 dt_develop_blend_params_t *bp = module->blend_params;
4176
4177 if(IS_NULL_PTR(bd) || !bd->masks_support || !bd->raster_inited) return;
4178
4179 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(bd->raster_polarity), bp->raster_mask_invert);
4180
4182}
4183
4184static void _raster_polarity_callback(GtkToggleButton *togglebutton, dt_iop_module_t *self)
4185{
4186 if(dt_gui_widgets_suppressed()) return;
4187
4189
4190 bp->raster_mask_invert = gtk_toggle_button_get_active(togglebutton);
4191
4194 dt_control_queue_redraw_widget(GTK_WIDGET(togglebutton));
4195}
4196
4197void dt_iop_gui_init_raster(GtkBox *blendw, dt_iop_module_t *module)
4198{
4200
4201 bd->raster_box = blendw;
4202
4203 /* create and add raster support if module supports it (it's coupled to masks at the moment) */
4204 if(bd->masks_support)
4205 {
4206 GtkWidget *hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, DT_GUI_BOX_SPACING);
4207
4211 dt_bauhaus_widget_set_label(bd->raster_combo, N_("raster mask"));
4212 dt_bauhaus_combobox_add(bd->raster_combo, _("no mask used"));
4213 g_signal_connect(G_OBJECT(bd->raster_combo), "value-changed",
4214 G_CALLBACK(_raster_value_changed_callback), module);
4216 gtk_box_pack_start(GTK_BOX(hbox), bd->raster_combo, TRUE, TRUE, 0);
4217
4219 dt_gui_add_class(bd->raster_polarity, "dt_ignore_fg_state");
4220 gtk_widget_set_tooltip_text(bd->raster_polarity, _("toggle polarity of raster mask"));
4221 g_signal_connect(G_OBJECT(bd->raster_polarity), "toggled", G_CALLBACK(_raster_polarity_callback), module);
4222 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(bd->raster_polarity), FALSE);
4223 gtk_box_pack_start(GTK_BOX(hbox), bd->raster_polarity, FALSE, FALSE, 0);
4224
4225 gtk_box_pack_start(GTK_BOX(bd->raster_box), GTK_WIDGET(hbox), FALSE, FALSE, 0);
4226
4227 bd->raster_inited = 1;
4228 }
4229}
4230
4232{
4233 if(IS_NULL_PTR(module->blend_data)) return;
4235
4238
4240 if(bd->timeout_handle)
4241 g_source_remove(bd->timeout_handle);
4242
4246
4247 dt_free(module->blend_data);
4248 module->blend_data = NULL;
4249}
4250
4251
4253{
4254 for(const dt_develop_name_value_t *bm = dt_develop_blend_mode_names; *bm->name; bm++)
4255 {
4256 if(bm->value == mode)
4257 {
4258 dt_bauhaus_combobox_add_full(combobox, g_dpgettext2(NULL, "blendmode", bm->name), DT_BAUHAUS_COMBOBOX_ALIGN_RIGHT, GUINT_TO_POINTER(bm->value), NULL, TRUE);
4259
4260 return TRUE;
4261 }
4262 }
4263
4264 return FALSE;
4265}
4266
4267static GtkWidget *_combobox_new_from_list(dt_iop_module_t *module, const gchar *label,
4268 const dt_develop_name_value_t *list, uint32_t *field, const gchar *tooltip)
4269{
4274
4275 if(field)
4277 dt_bauhaus_widget_set_label(combo, label);
4278 gtk_widget_set_tooltip_text(combo, tooltip);
4279 for(; *list->name; list++)
4281 GUINT_TO_POINTER(list->value), NULL, TRUE);
4282
4283 return combo;
4284}
4285
4286static void _notebook_append_full_width_page(GtkWidget *notebook, GtkWidget *page, const gchar *label)
4287{
4288 GtkWidget *tab = gtk_label_new(label);
4289 gtk_notebook_append_page(GTK_NOTEBOOK(notebook), page, tab);
4290 gtk_container_child_set(GTK_CONTAINER(notebook), page, "tab-expand", TRUE, "tab-fill", TRUE, NULL);
4291}
4292
4293static GtkWidget *_blendop_create_enable_toggle(dt_iop_module_t *module, const unsigned int mask_bit)
4294{
4295 const gboolean is_disable_toggle = mask_bit == DEVELOP_MASK_RASTER
4296 || mask_bit == DEVELOP_MASK_SHAPE
4297 || mask_bit == DEVELOP_MASK_PARAMETRIC;
4298 GtkWidget *toggle = gtk_check_button_new_with_label(is_disable_toggle ? _("Disable") : _("Enable"));
4299 g_object_set_data(G_OBJECT(toggle), "mask-bit", GUINT_TO_POINTER(mask_bit));
4300 g_signal_connect(G_OBJECT(toggle), "toggled", G_CALLBACK(_blendop_masks_mode_changed), module);
4301 return toggle;
4302}
4303
4305{
4306 if(IS_NULL_PTR(module) || !module->blend_data) return;
4307
4309 if(!GTK_IS_BUTTON(bd->top_enable)) return;
4310
4311 gchar *clean_name = delete_underscore(module->name());
4312 const char *multi_name = module->multi_name[0] ? module->multi_name : "0";
4313 gchar *multi_name_dup = ((g_strcmp0(multi_name, "0") == 0) || (g_strcmp0(multi_name, "") == 0)) ? g_strdup("") : g_strdup_printf(" (%s)", multi_name);
4314 GtkWidget *child = gtk_bin_get_child(GTK_BIN(bd->top_enable));
4315 gchar *label = g_markup_printf_escaped(_("Enable in <b>%s%s</b>"), clean_name, multi_name_dup);
4316 if(GTK_IS_LABEL(child))
4317 {
4318 gtk_label_set_markup(GTK_LABEL(child), label);
4319 gtk_label_set_line_wrap(GTK_LABEL(child), TRUE);
4320 gtk_label_set_xalign(GTK_LABEL(child), 0.0f);
4321 }
4322 dt_free(label);
4323 dt_free(clean_name);
4324 dt_free(multi_name_dup);
4325}
4326
4328 gchar *help_url, GtkWidget **content)
4329{
4330 GtkWidget *page = gtk_event_box_new();
4331 if(help_url) dt_gui_add_help_link(page, help_url);
4332 *content = gtk_box_new(GTK_ORIENTATION_VERTICAL, DT_GUI_BOX_SPACING);
4333 gtk_container_add(GTK_CONTAINER(page), *content);
4335 return page;
4336}
4337
4339 gchar *help_url,
4340 dt_iop_module_t *module, const unsigned int mask_bit,
4341 GtkWidget **toggle, GtkWidget **content)
4342{
4343 GtkWidget *page = gtk_event_box_new();
4344 if(help_url) dt_gui_add_help_link(page, help_url);
4345 GtkWidget *vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, DT_GUI_BOX_SPACING);
4346 gtk_container_add(GTK_CONTAINER(page), vbox);
4347
4348 GtkWidget *header = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, DT_GUI_BOX_SPACING);
4349 *toggle = _blendop_create_enable_toggle(module, mask_bit);
4350 gtk_box_pack_start(GTK_BOX(header), *toggle, FALSE, FALSE, 0);
4351 gtk_box_pack_start(GTK_BOX(vbox), header, FALSE, FALSE, 0);
4352
4353 *content = gtk_box_new(GTK_ORIENTATION_VERTICAL, DT_GUI_BOX_SPACING);
4354 gtk_box_pack_start(GTK_BOX(vbox), *content, TRUE, TRUE, 0);
4355
4357 return header;
4358}
4359
4360static void _blendop_toggle_button_set_active(GtkWidget *toggle, const gboolean enabled)
4361{
4362 if(GTK_IS_TOGGLE_BUTTON(toggle))
4363 {
4364 const unsigned int bit = GPOINTER_TO_UINT(g_object_get_data(G_OBJECT(toggle), "mask-bit"));
4365 const gboolean is_disable_toggle = bit == DEVELOP_MASK_RASTER
4366 || bit == DEVELOP_MASK_SHAPE
4367 || bit == DEVELOP_MASK_PARAMETRIC;
4368 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle), is_disable_toggle ? !enabled : enabled);
4369 }
4370}
4371
4372static void _blendop_sync_toggle_state(GtkWidget *toggle, const gboolean available,
4373 const gboolean enabled, GtkWidget *content)
4374{
4375 _blendop_toggle_button_set_active(toggle, enabled);
4376 if(GTK_IS_WIDGET(toggle))
4377 gtk_widget_set_sensitive(toggle, available);
4378 if(GTK_IS_WIDGET(content))
4379 gtk_widget_set_sensitive(content, available && enabled);
4380}
4381
4383{
4385
4386 if(!(module->flags() & IOP_FLAGS_SUPPORTS_BLENDING) || IS_NULL_PTR(bd)) return;
4387
4389
4390 // update color space from parameters
4392 switch(default_csp)
4393 {
4396 break;
4400 switch(module->blend_params->blend_cst)
4401 {
4405 bd->csp = module->blend_params->blend_cst;
4406 break;
4407 default:
4408 bd->csp = default_csp;
4409 break;
4410 }
4411 break;
4413 default:
4415 break;
4416 }
4417
4418 // (un)set the mask indicator
4420
4421 if(!bd->blending_box)
4422 {
4424 return;
4425 }
4426
4427 // initialization of blending modes
4428 if(bd->csp != bd->blend_modes_csp)
4429 {
4431
4432 if(bd->csp == DEVELOP_BLEND_CS_LAB
4434 || bd->csp == DEVELOP_BLEND_CS_RAW )
4435 {
4452
4453 if(bd->csp == DEVELOP_BLEND_CS_LAB)
4454 {
4464 }
4465 else if(bd->csp == DEVELOP_BLEND_CS_RGB_DISPLAY)
4466 {
4477 }
4478 }
4479 else if(bd->csp == DEVELOP_BLEND_CS_RGB_SCENE)
4480 {
4495 }
4496 bd->blend_modes_csp = bd->csp;
4497 }
4498
4499 dt_develop_blend_mode_t blend_mode = module->blend_params->blend_mode & DEVELOP_BLEND_MODE_MASK;
4500 // module->blend_params->mask_mode
4502 {
4503 // add deprecated blend mode
4504 if(!_add_blendmode_combo(bd->blend_modes_combo, blend_mode))
4505 {
4506 // should never happen: unknown blend mode
4507 dt_control_log("unknown blend mode '%d' in module '%s'", blend_mode, module->op);
4508 module->blend_params->blend_mode = DEVELOP_BLEND_NORMAL2;
4509 blend_mode = DEVELOP_BLEND_NORMAL2;
4510 }
4511
4513 }
4514
4515 gboolean blend_mode_reversed = (module->blend_params->blend_mode & DEVELOP_BLEND_REVERSE) == DEVELOP_BLEND_REVERSE;
4517
4519 const gboolean show_param = (bd->blend_modes_csp == DEVELOP_BLEND_CS_RGB_SCENE) &&
4521 if(!show_param)
4522 {
4523 module->blend_params->blend_parameter = 0.0f;
4525 }
4526 gtk_widget_set_visible(bd->blend_mode_parameter_slider, show_param);
4527
4540
4541 /* reset all alternative display modes for blendif */
4542 memset(bd->altmode, 0, sizeof(bd->altmode));
4543
4544 // force the visibility of output channels if they contain some setting
4547
4549 dt_masks_iop_update(module);
4551
4552 /* sync page states from mask mode */
4553 uint32_t mask_mode = module->blend_params->mask_mode;
4554 const gboolean top_enabled = (mask_mode & DEVELOP_MASK_ENABLED) != 0;
4555 const gboolean masks_enabled = (mask_mode & DEVELOP_MASK_SHAPE) != 0;
4556 const gboolean raster_enabled = (mask_mode & DEVELOP_MASK_RASTER) != 0;
4557 const gboolean blendif_enabled = (mask_mode & DEVELOP_MASK_PARAMETRIC) != 0;
4558 const gboolean bottom_enabled = top_enabled && ((bd->masks_inited && masks_enabled)
4559 || (bd->raster_inited && raster_enabled)
4560 || (bd->blendif_inited && blendif_enabled));
4561
4564
4565 if(GTK_IS_WIDGET(bd->blending_box))
4566 {
4567 gtk_widget_set_sensitive(bd->blending_box, top_enabled);
4568 // Note: the showmask toggle is outside the blending box.
4569 gtk_widget_set_sensitive(bd->showmask, top_enabled);
4570 }
4571
4575
4576 if(bd->blendif_inited && !IS_NULL_PTR(bd->channel))
4577 {
4578 /* The parametric page may have made the whole content insensitive before
4579 * `_blendop_blendif_update_tab()` restored the channel-local state.
4580 * Reapply the final contract after the page toggle is synchronized:
4581 * boost factor is editable only for an enabled parametric mask and a
4582 * channel whose descriptor explicitly supports boosting. */
4583 gtk_widget_set_sensitive(
4585 blendif_enabled && bd->channel[bd->tab].boost_factor_enabled);
4586 }
4587
4588 if(bd->blendif_inited && blendif_enabled)
4589 {
4590 gtk_widget_hide(GTK_WIDGET(bd->masks_invert_combo));
4591 gtk_widget_show(GTK_WIDGET(bd->masks_combine_combo));
4592 }
4593 else
4594 {
4595 gtk_widget_show(GTK_WIDGET(bd->masks_invert_combo));
4596 gtk_widget_hide(GTK_WIDGET(bd->masks_combine_combo));
4597 }
4598
4599 /*
4600 * if this iop is operating in raw space, it has only 1 channel per pixel,
4601 * thus there is no alpha channel where we would normally store mask
4602 * that would get displayed if following button have been pressed.
4603 */
4604 if(module->blend_colorspace(module, NULL, NULL) == IOP_CS_RAW)
4605 {
4606 module->request_mask_display = DT_DEV_PIXELPIPE_DISPLAY_NONE;
4609 gtk_widget_hide(GTK_WIDGET(bd->showmask));
4610
4611 // disable also guided filters on RAW based color space
4612 gtk_widget_set_sensitive(bd->masks_feathering_guide_combo, FALSE);
4613 gtk_widget_hide(bd->masks_feathering_guide_combo);
4614 gtk_widget_set_sensitive(bd->feathering_radius_slider, FALSE);
4615 gtk_widget_hide(bd->feathering_radius_slider);
4616 gtk_widget_set_sensitive(bd->brightness_slider, FALSE);
4617 gtk_widget_hide(bd->brightness_slider);
4618 gtk_widget_set_sensitive(bd->contrast_slider, FALSE);
4619 gtk_widget_hide(bd->contrast_slider);
4620 gtk_widget_set_sensitive(bd->details_slider, FALSE);
4621 gtk_widget_hide(bd->details_slider);
4622 }
4623 else
4624 {
4625 gtk_widget_show(GTK_WIDGET(bd->showmask));
4626 gtk_widget_set_sensitive(bd->masks_feathering_guide_combo, TRUE);
4627 gtk_widget_show(bd->masks_feathering_guide_combo);
4628 gtk_widget_set_sensitive(bd->feathering_radius_slider, TRUE);
4629 gtk_widget_show(bd->feathering_radius_slider);
4630 gtk_widget_set_sensitive(bd->brightness_slider, TRUE);
4631 gtk_widget_show(bd->brightness_slider);
4632 gtk_widget_set_sensitive(bd->contrast_slider, TRUE);
4633 gtk_widget_show(bd->contrast_slider);
4634 /* show details slider only if it was used in an old edit (non-zero) */
4635 const gboolean show_details = (module->blend_params->details != 0.0f);
4636 gtk_widget_set_visible(bd->details_slider, show_details);
4637 }
4638
4639 if(!bottom_enabled)
4640 {
4641 module->request_mask_display = DT_DEV_PIXELPIPE_DISPLAY_NONE;
4644 }
4645
4646 if(bd->masks_inited && !masks_enabled)
4647 {
4650 }
4651
4652 if(bd->masks_support && !masks_enabled)
4653 {
4655 }
4656
4657 if(bd->blendif_inited && !blendif_enabled)
4658 {
4660 }
4661
4663
4664 // Re-evaluate header state after notebook toggle synchronization.
4666
4668}
4669
4671{
4672 // FIXME : there are more than 3 functions getting clever about how to setup module->request_mask_display depending on user input.
4673 // These should all use an uniform setter function.
4674 //
4675 // The lack of setter implies we don't guaranty that only 1 module can request mask display at a time.
4676 // The consequence is pipeline needs to check if module->request_mask_display AND module == dev->gui_module,
4677 // but the global pipe->mask_display is set from *_blend_process() at runtime, so it's a pipe property
4678 // that changes over the pipe lifecycle.
4679 //
4680 // This is a self-feeding loop of madness because it ties the pipeline to GUI states
4681 // (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),
4682 // and because the pipeline is executed recursively from the end, but pipe->mask_display is set in the middle,
4683 // when it reaches the process() method of the module capturing mask preview, so you don't have this info when
4684 // planning for pipeline execution.
4685 // And you need to plan for mask preview ahead in pipe because mask preview needs to work
4686 // without using the pixelpipe cache, at least between the module requiring mask preview and gamma.c, which will actually render
4687 // the preview at the far end of the pipe.
4688 // So the not-so-clever workaround inherited from darktable was to flush all cache lines when requesting mask preview,
4689 // which flushed lines that could be reused later and were only temporarily not needed.
4690
4691 if(dt_gui_widgets_suppressed()) return;
4692 if(IS_NULL_PTR(module)) return;
4693
4694 const int has_mask_display = module->request_mask_display & (DT_DEV_PIXELPIPE_DISPLAY_MASK | DT_DEV_PIXELPIPE_DISPLAY_CHANNEL);
4695
4696 if((module->flags() & IOP_FLAGS_SUPPORTS_BLENDING) && module->blend_data)
4697 {
4699 if(bd->showmask)
4701 module->request_mask_display = DT_DEV_PIXELPIPE_DISPLAY_NONE;
4704
4705 // (re)set the header mask indicator too
4706 if(bd->masks_support && bd->masks_edit)
4707 {
4708 // unselect all tools
4711
4713 }
4714
4717 if(bd->timeout_handle)
4718 {
4719 // purge any remaining timeout handlers
4720 g_source_remove(bd->timeout_handle);
4721 bd->timeout_handle = 0;
4722 }
4724
4725 // reprocess main center image if needed
4726 if (has_mask_display)
4728 }
4729}
4730
4732{
4733 if(IS_NULL_PTR(module)) return;
4734 dt_iop_gui_blend_data_t *bd = module->blend_data;
4735 if(IS_NULL_PTR(bd) || !bd->blendif_support || !bd->blendif_inited) return;
4737}
4738
4740{
4741 if(IS_NULL_PTR(module) || !module->blend_data) return;
4742
4744 if(IS_NULL_PTR(bd->blending_box)) return;
4746 if(bd->timeout_handle)
4747 {
4748 g_source_remove(bd->timeout_handle);
4749 bd->timeout_handle = 0;
4750 }
4753
4754 if(bd->masks_ic_inverse) g_object_unref(bd->masks_ic_inverse);
4755 if(bd->masks_ic_union) g_object_unref(bd->masks_ic_union);
4756 if(bd->masks_ic_intersection) g_object_unref(bd->masks_ic_intersection);
4757 if(bd->masks_ic_difference) g_object_unref(bd->masks_ic_difference);
4758 if(bd->masks_ic_exclusion) g_object_unref(bd->masks_ic_exclusion);
4759 if(bd->group_shapes_store) g_object_unref(bd->group_shapes_store);
4760 if(bd->all_shapes_store) g_object_unref(bd->all_shapes_store);
4761
4762 gtk_widget_destroy(bd->blending_box);
4763
4764 bd->blending_box = NULL;
4765 bd->blending_notebook = NULL;
4766 bd->top_enable = NULL;
4767 bd->masks_enable = NULL;
4768 bd->raster_enable = NULL;
4769 bd->blendif_enable = NULL;
4770 bd->blending_box = NULL;
4771 bd->masks_content = NULL;
4772 bd->raster_content = NULL;
4773 bd->blendif_content = NULL;
4774 bd->contours_content = NULL;
4775 bd->blendif_box = NULL;
4776 bd->masks_box = NULL;
4777 bd->raster_box = NULL;
4778 bd->colorpicker = NULL;
4779 bd->colorpicker_set_values = NULL;
4780 memset(bd->filter, 0, sizeof(bd->filter));
4781 bd->showmask = NULL;
4782 bd->masks_combine_combo = NULL;
4783 bd->blend_modes_combo = NULL;
4784 bd->blend_modes_blend_order = NULL;
4785 bd->blend_mode_parameter_slider = NULL;
4786 bd->masks_invert_combo = NULL;
4787 bd->opacity_slider = NULL;
4789 bd->feathering_radius_slider = NULL;
4790 bd->blur_radius_slider = NULL;
4791 bd->contrast_slider = NULL;
4792 bd->brightness_slider = NULL;
4793 bd->channel_boost_factor_slider = NULL;
4794 bd->details_slider = NULL;
4795 bd->masks_combo = NULL;
4796 memset(bd->masks_shapes, 0, sizeof(bd->masks_shapes));
4797 bd->masks_edit = NULL;
4798 bd->group_shapes_label = NULL;
4799 bd->masks_polarity = NULL;
4800 bd->wire_shape_toggle = NULL;
4801 bd->masks_treeview = NULL;
4802 bd->masks_group_treeview = NULL;
4803 bd->group_shapes_store = NULL;
4804 bd->group_shapes_col = NULL;
4805 bd->group_unlink_col = NULL;
4806 bd->group_delete_col = NULL;
4807 bd->all_shapes_store = NULL;
4808 bd->group_shapes_sw = NULL;
4809 bd->all_shapes_col = NULL;
4810 bd->all_shapes_delete_col = NULL;
4811 bd->all_shapes_sw = NULL;
4812 bd->lists_stack = NULL;
4813 bd->all_shapes_buttons = NULL;
4814 bd->lists_box = NULL;
4815 bd->masks_ic_inverse = NULL;
4816 bd->masks_ic_union = NULL;
4817 bd->masks_ic_intersection = NULL;
4818 bd->masks_ic_difference = NULL;
4819 bd->masks_ic_exclusion = NULL;
4820 bd->raster_combo = NULL;
4821 bd->raster_polarity = NULL;
4823 memset(bd->picker_set_values_box, 0, sizeof(bd->picker_set_values_box));
4825 bd->channel_tabs = NULL;
4826 bd->blendif_inited = 0;
4827 bd->masks_inited = 0;
4828 bd->raster_inited = 0;
4831 module->fusion_slider = NULL;
4832}
4833
4835{
4836 if(IS_NULL_PTR(container) || !GTK_IS_WIDGET(container)) return;
4837 if(!(module->flags() & IOP_FLAGS_SUPPORTS_BLENDING)) return;
4838 if(IS_NULL_PTR(module->blend_data)) return;
4839
4841 if(bd->blending_box) return;
4842
4844
4845 /* NOTE: we attach 2 modules to the parent container:
4846 1. the on/off global switch,
4847 2. the box containing all other options (recursively nested).
4848 This is just a trick so we can hide/insensitize the whole content
4849 in one shot, on main disabling, except for the main switch.
4850 */
4851
4852 /* Global ON/OFF switch + show/hide toggle */
4853 GtkWidget *display_box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, DT_GUI_BOX_SPACING);
4854 dt_gui_add_help_link(display_box, dt_get_help_url("masks_blending"));
4855 gtk_widget_set_name(display_box, "blending-main-switch");
4856
4859 gtk_box_pack_start(GTK_BOX(display_box), bd->top_enable, FALSE, FALSE, 0);
4860
4861 bd->showmask = dt_iop_togglebutton_new_no_register(module, "blend`tools", N_("display mask and/or color channel"), NULL,
4863 FALSE, 0, 0, dtgtk_cairo_paint_showmask, display_box);
4864 gtk_widget_set_tooltip_text(bd->showmask, _("display mask and/or color channel. ctrl+click to display mask, "
4865 "shift+click to display channel. hover over parametric mask slider to "
4866 "select channel for display"));
4867
4868 gtk_box_pack_start(GTK_BOX(container), display_box, FALSE, FALSE, 0);
4869 gtk_widget_show_all(display_box);
4870
4871 /* Main widget container */
4872 bd->blending_box = gtk_box_new(GTK_ORIENTATION_VERTICAL, DT_GUI_BOX_SPACING);
4873 gtk_box_pack_start(GTK_BOX(container), bd->blending_box, TRUE, TRUE, 0);
4874 gtk_widget_set_name(bd->blending_box, "blending-box");
4875
4876 /* Blend mode */
4881 dt_bauhaus_widget_set_label(bd->blend_modes_combo, N_("Blend mode"));
4882 g_signal_connect(G_OBJECT(bd->blend_modes_combo), "value-changed",
4883 G_CALLBACK(_blendop_blend_mode_callback), bd);
4884 dt_gui_add_help_link(GTK_WIDGET(bd->blend_modes_combo), dt_get_help_url("masks_blending_op"));
4885 gtk_box_pack_start(GTK_BOX(bd->blending_box), bd->blend_modes_combo, TRUE, TRUE, 0);
4886
4887 /* Compositing combobox: choose compositing order */
4891 GINT_TO_POINTER(0), NULL, TRUE);
4893 GINT_TO_POINTER(1), NULL, TRUE);
4896 gtk_widget_set_tooltip_text(bd->blend_modes_blend_order, _("choose compositing order: which layer is placed on top"));
4897 g_signal_connect(G_OBJECT(bd->blend_modes_blend_order), "value-changed",
4898 G_CALLBACK(_blendop_compositing_changed), bd);
4899 gtk_box_pack_start(GTK_BOX(bd->blending_box), bd->blend_modes_blend_order, TRUE, TRUE, 0);
4900
4901 /* Optional fulcrum parameter for blend modes supporting it */
4910 gtk_widget_set_tooltip_text(bd->blend_mode_parameter_slider, _("adjust the fulcrum used by some blending"
4911 " operations"));
4912 g_object_set_data(G_OBJECT(bd->blend_mode_parameter_slider), "dt-blendop-header-update", GINT_TO_POINTER(TRUE));
4913 gtk_widget_set_visible(bd->blend_mode_parameter_slider, FALSE);
4914 gtk_box_pack_start(GTK_BOX(bd->blending_box), bd->blend_mode_parameter_slider, TRUE, TRUE, 0);
4915
4916 /* mask combine control: how to combine drawn/parametric masks */
4917 bd->masks_combine_combo = _combobox_new_from_list(module, _("Combine drawn & parametric"), dt_develop_combine_masks_names, NULL,
4918 _("how to combine drawn and parametric masks"));
4919 g_signal_connect(G_OBJECT(bd->masks_combine_combo), "value-changed",
4920 G_CALLBACK(_blendop_masks_combine_callback), bd);
4921 dt_gui_add_help_link(GTK_WIDGET(bd->masks_combine_combo), dt_get_help_url("masks_combined"));
4922 gtk_box_pack_start(GTK_BOX(bd->blending_box), GTK_WIDGET(bd->masks_combine_combo), TRUE, TRUE, 0);
4923
4924 /* mask invert control: single checkbox to invert masks */
4925 bd->masks_invert_combo = gtk_check_button_new_with_label(_("Invert mask"));
4926 gtk_widget_set_tooltip_text(bd->masks_invert_combo, _("invert the resulting mask (apply mask in inverted mode)"));
4927 g_signal_connect(G_OBJECT(bd->masks_invert_combo), "toggled",
4928 G_CALLBACK(_blendop_masks_invert_toggled), bd);
4929 gtk_box_pack_start(GTK_BOX(bd->blending_box), GTK_WIDGET(bd->masks_invert_combo), TRUE, TRUE, 0);
4930
4931 /* Global mask output opacity */
4932 bd->opacity_slider = dt_bauhaus_slider_new_with_range(darktable.bauhaus, DT_GUI_MODULE(module), 0.0, 100.0, 0, 100.0, 0);
4937 dt_bauhaus_widget_set_label(bd->opacity_slider, N_("opacity"));
4939 module->fusion_slider = bd->opacity_slider;
4940 gtk_widget_set_tooltip_text(bd->opacity_slider, _("set the opacity of the blending"));
4941 g_object_set_data(G_OBJECT(bd->opacity_slider), "dt-blendop-header-update", GINT_TO_POINTER(TRUE));
4942 gtk_box_pack_start(GTK_BOX(bd->blending_box), bd->opacity_slider, TRUE, TRUE, 0);
4943
4944 bd->details_slider = dt_bauhaus_slider_new_with_range(darktable.bauhaus, DT_GUI_MODULE(module), -1.0f, 1.0f, 0, 0.0f, 2);
4948 dt_bauhaus_widget_set_label(bd->details_slider, N_("details threshold"));
4950 gtk_widget_set_tooltip_text(bd->details_slider, _("adjust the threshold for the details mask, "
4951 "\npositive values selects areas with strong details, "
4952 "\nnegative values select flat areas"));
4953 g_signal_connect(G_OBJECT(bd->details_slider), "value-changed", G_CALLBACK(_blendop_blendif_details_callback), bd);
4954 gtk_widget_hide(bd->details_slider);
4955
4961 dt_bauhaus_widget_set_label(bd->blur_radius_slider, N_("blurring radius"));
4963 gtk_widget_set_tooltip_text(bd->blur_radius_slider, _("radius for gaussian blur of blend mask"));
4964 g_object_set_data(G_OBJECT(bd->blur_radius_slider), "dt-blendop-header-update", GINT_TO_POINTER(TRUE));
4965
4968 _("choose to guide mask by input or output image and"
4969 "\nchoose to apply feathering before or after mask blur"));
4970 g_object_set_data(G_OBJECT(bd->masks_feathering_guide_combo), "dt-blendop-header-update", GINT_TO_POINTER(TRUE));
4971
4977 dt_bauhaus_widget_set_label(bd->feathering_radius_slider, N_("feathering radius"));
4979 gtk_widget_set_tooltip_text(bd->feathering_radius_slider, _("spatial radius of feathering"));
4980 g_object_set_data(G_OBJECT(bd->feathering_radius_slider), "dt-blendop-header-update", GINT_TO_POINTER(TRUE));
4981
4987 dt_bauhaus_widget_set_label(bd->brightness_slider, N_("Feathering mask opacity"));
4989 gtk_widget_set_tooltip_text(bd->brightness_slider, _("shifts and tilts the tone curve of the blend mask to adjust its "
4990 "brightness without affecting fully transparent/fully opaque "
4991 "regions"));
4992 g_object_set_data(G_OBJECT(bd->brightness_slider), "dt-blendop-header-update", GINT_TO_POINTER(TRUE));
4993
4999 dt_bauhaus_widget_set_label(bd->contrast_slider, N_("Feathering mask contrast"));
5001 gtk_widget_set_tooltip_text(bd->contrast_slider, _("gives the tone curve of the blend mask an s-like shape to "
5002 "adjust its contrast"));
5003 g_object_set_data(G_OBJECT(bd->contrast_slider), "dt-blendop-header-update", GINT_TO_POINTER(TRUE));
5004
5005 /* separator between global blending options and the masking sub-modes notebook */
5006 GtkWidget *blend_separator = gtk_separator_new(GTK_ORIENTATION_HORIZONTAL);
5007 gtk_widget_set_name(blend_separator, "blending-separator");
5008 gtk_box_pack_start(GTK_BOX(bd->blending_box), blend_separator, FALSE, FALSE, 0);
5009
5010 GtkWidget *presets_button = dtgtk_button_new(dtgtk_cairo_paint_presets, 0, NULL);
5011 gtk_widget_set_tooltip_text(presets_button, _("blending options"));
5012 if(bd->blendif_support)
5013 g_signal_connect(G_OBJECT(presets_button), "button-press-event", G_CALLBACK(_blendif_options_callback), module);
5014 else
5015 gtk_widget_set_sensitive(GTK_WIDGET(presets_button), FALSE);
5016
5017 /* Blending submodes notebook */
5018 bd->blending_notebook = gtk_notebook_new();
5019 gtk_notebook_set_scrollable(GTK_NOTEBOOK(bd->blending_notebook), TRUE);
5020 gtk_notebook_set_action_widget(GTK_NOTEBOOK(bd->blending_notebook), presets_button, GTK_PACK_END);
5021 gtk_widget_show(presets_button);
5022 gtk_box_pack_start(GTK_BOX(bd->blending_box), bd->blending_notebook, TRUE, TRUE, 0);
5023
5024 /* Raster page */
5025 _blendop_create_toggle_page(bd->blending_notebook, _("Raster"), dt_get_help_url("masks_raster"),
5027 dt_iop_gui_init_raster(GTK_BOX(bd->raster_content), module);
5028
5029 /* Drawn page */
5030 _blendop_create_toggle_page(bd->blending_notebook, _("Drawn"), dt_get_help_url("masks_drawn"),
5031 module, DEVELOP_MASK_SHAPE, &bd->masks_enable, &bd->masks_content);
5032 dt_iop_gui_init_masks(GTK_BOX(bd->masks_content), module);
5033
5034 /* Parametric page */
5035 GtkWidget *blendif_header = _blendop_create_toggle_page(bd->blending_notebook, _("Parametric"),
5036 dt_get_help_url("masks_parametric"),
5038 &bd->blendif_content);
5039 dt_iop_gui_init_blendif(GTK_BOX(bd->blendif_content), module, blendif_header);
5040
5041 /* Contours page */
5042 _blendop_create_notebook_page(bd->blending_notebook, _("Contours"), dt_get_help_url("masks_refinement"), &bd->contours_content);
5043 dt_iop_gui_init_contours(GTK_BOX(bd->contours_content), module);
5044
5045 /* Init all GUI states from module params */
5046 const unsigned int mask_mode = module->blend_params->mask_mode;
5051
5052 gtk_widget_show_all(GTK_WIDGET(bd->blending_box));
5053 // show_all overrides the visibility the collapsible section set at init; re-apply config state.
5055 gtk_widget_set_sensitive(bd->blending_box, (mask_mode & DEVELOP_MASK_ENABLED) != 0);
5056
5057 g_signal_connect(G_OBJECT(bd->blending_notebook), "switch_page", G_CALLBACK(_blendop_blending_notebook_switch), bd);
5058 const int page_count = gtk_notebook_get_n_pages(GTK_NOTEBOOK(bd->blending_notebook));
5059 if(page_count > 0)
5060 {
5061 // Restore the last-used tab after the notebook has been fully realized.
5063 page = CLAMP(page, 0, page_count - 1);
5064 gtk_notebook_set_current_page(GTK_NOTEBOOK(bd->blending_notebook), page);
5065 }
5066
5068}
5069
5071{
5072 if(!(module->flags() & IOP_FLAGS_SUPPORTS_BLENDING) || module->blend_data) return;
5073
5074 module->blend_data = g_malloc0(sizeof(dt_iop_gui_blend_data_t));
5076
5077 bd->module = module;
5081
5082 const dt_iop_colorspace_type_t cst = module->blend_colorspace(module, NULL, NULL);
5084 bd->masks_support = !(module->flags() & IOP_FLAGS_NO_MASKS);
5085
5086 dt_pthread_mutex_init(&bd->lock, NULL);
5089
5090 if(bd->masks_support)
5091 {
5093 G_CALLBACK(_blendop_masks_handler_callback), module);
5094 }
5095}
5096
5097// clang-format off
5098// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
5099// vim: shiftwidth=2 expandtab tabstop=2 cindent
5100// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
5101// 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:1650
void dt_bauhaus_combobox_clear(GtkWidget *widget)
Definition bauhaus.c:2192
float dt_bauhaus_slider_get(GtkWidget *widget)
Definition bauhaus.c:3486
gboolean dt_bauhaus_combobox_set_from_value(GtkWidget *widget, int value)
Definition bauhaus.c:2333
void dt_bauhaus_widget_set_field(GtkWidget *widget, gpointer field, dt_introspection_type_t field_type)
Definition bauhaus.c:1713
GtkWidget * dt_bauhaus_combobox_from_conf(dt_bauhaus_t *bh, dt_gui_module_t *self, const char *confkey)
Definition bauhaus.c:1871
void dt_bauhaus_disable_module_list(GtkWidget *widget)
Definition bauhaus.c:3963
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:3969
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:2041
gpointer dt_bauhaus_combobox_get_data(GtkWidget *widget)
Definition bauhaus.c:2182
void dt_bauhaus_combobox_add_populate_fct(GtkWidget *widget, void(*fct)(GtkWidget *w, void *module))
Definition bauhaus.c:2005
void dt_bauhaus_disable_accels(GtkWidget *widget)
Definition bauhaus.c:3957
void dt_bauhaus_slider_set(GtkWidget *widget, float pos)
Definition bauhaus.c:3537
void dt_bauhaus_combobox_set(GtkWidget *widget, const int pos)
Definition bauhaus.c:2304
void dt_bauhaus_widget_set_label(GtkWidget *widget, const char *label)
Definition bauhaus.c:1656
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:1783
GtkWidget * dt_bauhaus_combobox_new(dt_bauhaus_t *bh, dt_gui_module_t *self)
Definition bauhaus.c:1845
void dt_bauhaus_slider_set_format(GtkWidget *widget, const char *format)
Definition bauhaus.c:3613
void dt_bauhaus_combobox_add(GtkWidget *widget, const char *text)
Definition bauhaus.c:2019
@ 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:136
void dt_develop_blend_init_blendif_parameters(dt_develop_blend_params_t *blend_params, dt_develop_blend_colorspace_t cst)
Definition blend.c:166
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:2224
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:3851
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:5070
static void _blendif_hide_output_channels(GtkMenuItem *menuitem, dt_iop_module_t *module)
Definition blend_gui.c:2987
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:2373
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:2497
#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:4197
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:4293
static void _blendif_select_colorspace(GtkMenuItem *menuitem, dt_iop_module_t *module)
Definition blend_gui.c:2967
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:2056
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:2665
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:3002
static void _blendif_show_output_channels(GtkMenuItem *menuitem, dt_iop_module_t *module)
Definition blend_gui.c:2976
_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:2286
void dt_iop_gui_init_blendif(GtkBox *blendw, dt_iop_module_t *module, GtkWidget *blendif_header)
Definition blend_gui.c:3602
static void _blendop_sync_toggle_state(GtkWidget *toggle, const gboolean available, const gboolean enabled, GtkWidget *content)
Definition blend_gui.c:4372
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:3231
static gboolean _blendop_blendif_enter(GtkWidget *widget, GdkEventCrossing *event, dt_iop_module_t *module)
Definition blend_gui.c:3279
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:4267
static gboolean _blendop_masks_group_button_pressed(GtkWidget *treeview, GdkEventButton *event, dt_iop_module_t *module)
Definition blend_gui.c:2523
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:4338
void dt_iop_gui_init_blending_body(GtkWidget *container, dt_iop_module_t *module)
Definition blend_gui.c:4834
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:4327
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:3134
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:4184
const char * slider_tooltip[]
Definition blend_gui.c:3506
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:3203
static gboolean _blendop_blendif_leave_delayed(gpointer data)
Definition blend_gui.c:3346
#define COLORSTOPS(gradient)
Definition blend_gui.c:3430
static gboolean _blendop_blendif_leave(GtkWidget *widget, GdkEventCrossing *event, dt_iop_module_t *module)
Definition blend_gui.c:3392
const dt_iop_gui_blendif_channel_t rgb_channels[]
Definition blend_gui.c:3453
static gboolean _blendop_masks_all_button_pressed(GtkWidget *treeview, GdkEventButton *event, dt_iop_module_t *module)
Definition blend_gui.c:2112
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:2581
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:2187
static gboolean _blendop_blendif_key_press(GtkWidget *widget, GdkEventKey *event, dt_iop_module_t *module)
Definition blend_gui.c:3409
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:4129
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:2078
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:2349
const dt_iop_gui_blendif_channel_t Lab_channels[]
Definition blend_gui.c:3433
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:4382
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:3480
static gboolean _add_blendmode_combo(GtkWidget *combobox, dt_develop_blend_mode_t mode)
Definition blend_gui.c:4252
void dt_iop_gui_blending_lose_focus(dt_iop_module_t *module)
Definition blend_gui.c:4670
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:2045
static gboolean _blendif_change_blend_colorspace(dt_iop_module_t *module, dt_develop_blend_colorspace_t cst)
Definition blend_gui.c:2914
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:3775
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:2037
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:2326
static void _blendop_masks_group_update_row(dt_iop_module_t *module, const int formid, const int parentid)
Definition blend_gui.c:2636
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:3086
static void _raster_combo_populate(GtkWidget *w, void *m)
Definition blend_gui.c:4091
void dt_iop_gui_cleanup_blending_body(dt_iop_module_t *module)
Definition blend_gui.c:4739
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:4304
void dt_iop_gui_update_raster(dt_iop_module_t *module)
Definition blend_gui.c:4172
static void _blendop_masks_group_delete(dt_iop_module_t *module, const int formid, const int parentid)
Definition blend_gui.c:2475
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:4286
void dt_iop_gui_cleanup_blending(dt_iop_module_t *module)
Definition blend_gui.c:4231
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:4731
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:3763
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:2610
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_all_delete(dt_iop_module_t *module, const int formid)
Definition blend_gui.c:2022
static void _blendop_masks_group_move_callback(GtkWidget *menu_item, dt_iop_module_t *module)
Definition blend_gui.c:2311
#define NEUTRAL_GRAY
Definition blend_gui.c:78
static void _blendop_toggle_button_set_active(GtkWidget *toggle, const gboolean enabled)
Definition blend_gui.c:4360
static void _blendop_masks_group_unlink(dt_iop_module_t *module, const int formid, const int parentid)
Definition blend_gui.c:2455
void dt_iop_gui_update_blendif(dt_iop_module_t *module)
Definition blend_gui.c:3513
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:777
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:877
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:922
void dt_control_hinter_message(const struct dt_control_t *s, const char *message)
Definition control.c:934
uint32_t view(const dt_view_t *self)
Definition darkroom.c:228
darktable_t darktable
Definition darktable.c:183
#define DT_ALIGNED_PIXEL
Definition darktable.h:401
#define dt_gui_freeze_begin()
Definition darktable.h:900
#define dt_gui_freeze_end()
Definition darktable.h:901
#define dt_free(ptr)
Definition darktable.h:478
void void void gboolean dt_gui_widgets_suppressed(void)
Definition gtk.c:194
static gchar * delete_underscore(const char *s)
Definition darktable.h:1210
static const dt_aligned_pixel_simd_t value
Definition darktable.h:599
static gboolean dt_modifier_is(const GdkModifierType state, const GdkModifierType desired_modifier_mask)
Definition darktable.h:955
#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:293
#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:1255
gchar * dt_dev_get_masks_group_name(const struct dt_iop_module_t *module)
Definition develop.c:1543
gchar * dt_history_item_get_name(const struct dt_iop_module_t *module)
Definition develop.c:1557
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:384
static int dt_pthread_mutex_init(dt_pthread_mutex_t *mutex, const pthread_mutexattr_t *mutexattr)
Definition dtpthread.h:369
static int dt_pthread_mutex_destroy(dt_pthread_mutex_t *mutex)
Definition dtpthread.h:389
static int dt_pthread_mutex_lock(dt_pthread_mutex_t *mutex) ACQUIRE(mutex) NO_THREAD_SAFETY_ANALYSIS
Definition dtpthread.h:374
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:3185
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:3334
GtkWidget * dt_ui_notebook_page(GtkNotebook *notebook, const char *text, const char *tooltip)
Definition gtk.c:2470
void dt_gui_update_collapsible_section(dt_gui_collapsible_section_t *cs)
Definition gtk.c:3312
GdkModifierType dt_key_modifier_state()
Definition gtk.c:2397
void dt_gui_container_destroy_children(GtkContainer *container)
Definition gtk.c:3151
void dt_gui_add_help_link(GtkWidget *widget, char *link)
Definition gtk.c:2232
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:2945
void dt_gui_add_class(GtkWidget *widget, const gchar *class_name)
Definition gtk.c:133
@ DT_UI_RESIZE_DYNAMIC
Definition gtk.h:266
#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:479
#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:2701
void dt_iop_gui_update(dt_iop_module_t *module)
Definition imageop.c:2133
void dt_iop_gui_update_header(dt_iop_module_t *module)
Definition imageop.c:1183
void dt_iop_request_focus(dt_iop_module_t *module)
Definition imageop.c:2221
void dt_iop_set_cache_bypass(dt_iop_module_t *module, gboolean state)
Definition imageop.c:2989
void dt_iop_set_mask_mode(dt_iop_module_t *module, int mask_mode)
Definition imageop.c:1645
static gboolean dt_iop_colorspace_is_rgb(const dt_iop_colorspace_type_t cst)
Definition imageop.h:243
@ IOP_FLAGS_SUPPORTS_BLENDING
Definition imageop.h:197
@ IOP_CS_RGB_DISPLAY
Definition imageop.h:240
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:204
@ DT_MASKS_EDIT_OFF
Definition masks.h:202
@ DT_MASKS_EDIT_FULL
Definition masks.h:203
gboolean dt_masks_gui_confirm_permanent_delete(const char *form_name)
Ask the user to confirm a permanent shape deletion, gated by the "ask_before_delete_mask_shape" pref....
Definition masks_gui.c:464
dt_masks_state_t
Definition masks.h:167
@ DT_MASKS_STATE_DIFFERENCE
Definition masks.h:174
@ DT_MASKS_STATE_INVERSE
Definition masks.h:171
@ DT_MASKS_STATE_INTERSECTION
Definition masks.h:173
@ DT_MASKS_STATE_EXCLUSION
Definition masks.h:175
@ DT_MASKS_STATE_UNION
Definition masks.h:172
void dt_masks_shape_buttons_deactivate_all(GtkWidget *active_button)
Definition masks_gui.c:87
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:181
dt_masks_type_t
Definition masks.h:130
@ DT_MASKS_NON_CLONE
Definition masks.h:139
@ DT_MASKS_CLONE
Definition masks.h:135
@ DT_MASKS_NONE
Definition masks.h:131
@ DT_MASKS_GROUP
Definition masks.h:134
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:157
@ DT_MASKS_EVENT_UPDATE
Definition masks.h:161
@ DT_MASKS_EVENT_RESET
Definition masks.h:164
@ DT_MASKS_EVENT_ADD
Definition masks.h:159
@ DT_MASKS_EVENT_DELETE
Definition masks.h:162
@ DT_MASKS_EVENT_REMOVE
Definition masks.h:160
@ DT_MASKS_EVENT_CHANGE
Definition masks.h:163
dt_masks_form_t * dt_masks_create(dt_masks_type_t type)
@ DT_MASKS_SHAPE_BUTTONS_ALL
Definition masks.h:963
@ DT_MASKS_SHAPE_BUTTONS_POLYGON
Definition masks.h:957
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:937
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)
int dt_masks_form_duplicate(dt_develop_t *dev, int formid)
dt_masks_form_t * dt_masks_cow_touch(dt_develop_t *dev, dt_masks_form_t *form)
#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:282
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:387
#define DT_DEBUG_CONTROL_SIGNAL_RAISE(ctlsig, signal,...)
Definition signal.h:366
@ DT_SIGNAL_MASK_CHANGED
Definition signal.h:312
@ DT_SIGNAL_DEVELOP_MASKS_GUI_CHANGED
Definition signal.h:316
#define DT_DEBUG_CONTROL_SIGNAL_CONNECT(ctlsig, signal, cb, user_data)
Definition signal.h:376
struct _GtkWidget GtkWidget
Definition splash.h:29
const float uint32_t state[4]
struct dt_gui_gtk_t * gui
Definition darktable.h:803
struct dt_control_signal_t * signals
Definition darktable.h:802
struct dt_bauhaus_t * bauhaus
Definition darktable.h:806
struct dt_develop_t * develop
Definition darktable.h:798
struct dt_control_t * control
Definition darktable.h:801
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:56
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:285
GList * history
Definition develop.h:275
struct dt_masks_form_gui_t * form_gui
Definition develop.h:333
struct dt_dev_pixelpipe_t * pipe
Definition develop.h:247
GList * forms
Definition develop.h:327
GtkMenu * presets_popup_menu
Definition gtk.h:170
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:400
GtkWidget * lists_box
Definition blend.h:395
GtkWidget * showmask
Definition blend.h:341
GtkWidget * all_shapes_sw
Definition blend.h:387
GtkWidget * blur_radius_slider
Definition blend.h:350
GdkPixbuf * masks_ic_intersection
Definition blend.h:391
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:394
GtkWidget * colorpicker
Definition blend.h:338
GtkWidget * colorpicker_set_values
Definition blend.h:339
GdkPixbuf * masks_ic_union
Definition blend.h:390
GdkPixbuf * masks_ic_inverse
Definition blend.h:389
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:392
GtkTreeViewColumn * group_shapes_col
Definition blend.h:380
GtkWidget * lists_stack
Definition blend.h:388
dt_boundingbox_t picker_set_values_box
Definition blend.h:403
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:404
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:407
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:396
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:393
GtkWidget * raster_combo
Definition blend.h:399
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
GtkTreeViewColumn * all_shapes_delete_col
Definition blend.h:386
GtkNotebook * channel_tabs
Definition blend.h:362
gboolean picker_set_values_box_valid
Definition blend.h:402
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:376
gpointer blend_data
Definition imageop.h:355
GHashTable * masks
Definition imageop.h:365
struct dt_develop_blend_params_t * blend_params
Definition imageop.h:353
GtkWidget * widget
Definition imageop.h:374
char multi_name[128]
Definition imageop.h:400
struct dt_develop_t * dev
Definition imageop.h:333
GtkWidget * mask_indicator
Definition imageop.h:380
GModule *dt_dev_operation_t op
Definition imageop.h:286
struct dt_iop_module_t::@29 raster_mask
struct dt_iop_module_t::@29::@30 source
int request_mask_display
Definition imageop.h:298
GHashTable * users
Definition imageop.h:361
struct dt_develop_blend_params_t * default_blendop_params
Definition imageop.h:353
struct dt_iop_module_t::@29::@31 sink
gboolean node_selected
Definition masks.h:484
gboolean handle_border_selected
Definition masks.h:487
gboolean source_selected
Definition masks.h:492
dt_masks_type_t creation_type
Definition masks.h:521
gboolean source_dragging
Definition masks.h:500
dt_masks_edit_mode_t edit_mode
Definition masks.h:477
dt_iop_module_t * creation_module
Definition masks.h:519
gboolean seg_selected
Definition masks.h:486
gboolean form_dragging
Definition masks.h:499
gboolean creation
Definition masks.h:517
gboolean form_selected
Definition masks.h:490
gboolean handle_selected
Definition masks.h:485
gboolean form_rotating
Definition masks.h:501
gboolean border_selected
Definition masks.h:491
gboolean pivot_selected
Definition masks.h:493
dt_masks_type_t type
Definition masks.h:380
char name[128]
Definition masks.h:403
GList * points
Definition masks.h:379
dt_iop_module_t * owner_module
Definition masks.h:979
Definition blend_gui.c:4086
dt_iop_module_t *int id
Definition blend_gui.c:4088
GtkWidget * notebook
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)