Ansel 0.0
A darktable fork - bloat + design vision
Loading...
Searching...
No Matches
temperature.c
Go to the documentation of this file.
1/*
2 This file is part of darktable,
3 Copyright (C) 2009-2013, 2016 johannes hanika.
4 Copyright (C) 2010 Alexandre Prokoudine.
5 Copyright (C) 2010-2011 Bruce Guenter.
6 Copyright (C) 2010-2012 Henrik Andersson.
7 Copyright (C) 2010, 2012-2014 Pascal de Bruijn.
8 Copyright (C) 2010 Stuart Henderson.
9 Copyright (C) 2011 Antony Dovgal.
10 Copyright (C) 2011 Jérémy Rosen.
11 Copyright (C) 2011 Kanstantsin Shautsou.
12 Copyright (C) 2011 Olivier Tribout.
13 Copyright (C) 2011 Robert Bieber.
14 Copyright (C) 2011 Rostyslav Pidgornyi.
15 Copyright (C) 2011-2017, 2019 Tobias Ellinghaus.
16 Copyright (C) 2011-2012, 2014, 2016-2017 Ulrich Pegelow.
17 Copyright (C) 2012 Christian Tellefsen.
18 Copyright (C) 2012 Edouard Gomez.
19 Copyright (C) 2012 Richard Wonka.
20 Copyright (C) 2013, 2020, 2022 Aldric Renaudin.
21 Copyright (C) 2013, 2018-2022 Pascal Obry.
22 Copyright (C) 2014, 2018 Dan Torop.
23 Copyright (C) 2014-2016 Pedro Côrte-Real.
24 Copyright (C) 2014-2017, 2020 Roman Lebedev.
25 Copyright (C) 2017-2018 Matthieu Moy.
26 Copyright (C) 2018, 2020, 2022-2023, 2025-2026 Aurélien PIERRE.
27 Copyright (C) 2018-2019 Edgardo Hoszowski.
28 Copyright (C) 2018 Kelvie Wong.
29 Copyright (C) 2018 Maurizio Paglia.
30 Copyright (C) 2018 Peter Budai.
31 Copyright (C) 2018 rawfiner.
32 Copyright (C) 2019 Andreas Schneider.
33 Copyright (C) 2019-2022 Diederik Ter Rahe.
34 Copyright (C) 2019-2020, 2022 Hanno Schwalm.
35 Copyright (C) 2020-2021 Chris Elston.
36 Copyright (C) 2020 Harold le Clément de Saint-Marcq.
37 Copyright (C) 2020-2021 Hubert Kowalski.
38 Copyright (C) 2020-2021 Ralf Brown.
39 Copyright (C) 2021 luzpaz.
40 Copyright (C) 2022 Martin Bařinka.
41 Copyright (C) 2022 Miloš Komarčević.
42 Copyright (C) 2022 Nicolas Auffray.
43 Copyright (C) 2022 Philipp Lutz.
44 Copyright (C) 2022 Victor Forsiuk.
45
46 darktable is free software: you can redistribute it and/or modify
47 it under the terms of the GNU General Public License as published by
48 the Free Software Foundation, either version 3 of the License, or
49 (at your option) any later version.
50
51 darktable is distributed in the hope that it will be useful,
52 but WITHOUT ANY WARRANTY; without even the implied warranty of
53 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
54 GNU General Public License for more details.
55
56 You should have received a copy of the GNU General Public License
57 along with darktable. If not, see <http://www.gnu.org/licenses/>.
58*/
59#ifdef HAVE_CONFIG_H
60#include "config.h"
61#endif
62#include <assert.h>
63#include <lcms2.h>
64#include <math.h>
65#include <stdlib.h>
66#include <string.h>
67
68#include "bauhaus/bauhaus.h"
70#include "common/darktable.h"
71#include "common/opencl.h"
72#include "control/control.h"
73#include "control/conf.h"
74#include "develop/develop.h"
75#include "develop/imageop_gui.h"
77#include "develop/tiling.h"
78#include "dtgtk/expander.h"
79
80#include "gui/gtk.h"
82#include "iop/iop_api.h"
83
84// for Kelvin temperature and bogus WB
85#include "common/colorspaces.h"
86#include "external/cie_colorimetric_tables.c"
87
89
90#define INITIALBLACKBODYTEMPERATURE 4000
91
92#define DT_IOP_LOWEST_TEMPERATURE 1901
93#define DT_IOP_HIGHEST_TEMPERATURE 25000
94
95#define DT_IOP_LOWEST_TINT 0.135
96#define DT_IOP_HIGHEST_TINT 2.326
97
98#define DT_IOP_NUM_OF_STD_TEMP_PRESETS 4
99
100// If you reorder presets combo, change this consts
101#define DT_IOP_TEMP_AS_SHOT 0
102#define DT_IOP_TEMP_SPOT 1
103#define DT_IOP_TEMP_USER 2
104#define DT_IOP_TEMP_D65 3
105
106static void gui_sliders_update(struct dt_iop_module_t *self);
107static int calculate_bogus_daylight_wb(dt_iop_module_t *module, double bwb[4]);
108static void prepare_matrices(dt_iop_module_t *module);
109static void find_coeffs(dt_iop_module_t *module, double coeffs[4]);
110
112{
113 float red; // $MIN: 0.0 $MAX: 8.0
114 float green; // $MIN: 0.0 $MAX: 8.0
115 float blue; // $MIN: 0.0 $MAX: 8.0
116 float g2; // $MIN: 0.0 $MAX: 8.0 $DESCRIPTION: "emerald"
118
142
147
154
161
162int legacy_params(dt_iop_module_t *self, const void *const old_params, const int old_version,
163 void *new_params, const int new_version)
164{
165 if(old_version == 2 && new_version == 3)
166 {
167 typedef struct dt_iop_temperature_params_v2_t
168 {
169 float temp_out;
170 float coeffs[3];
171 } dt_iop_temperature_params_v2_t;
172
173 dt_iop_temperature_params_v2_t *o = (dt_iop_temperature_params_v2_t *)old_params;
175
176 n->red = o->coeffs[0];
177 n->green = o->coeffs[1];
178 n->blue = o->coeffs[2];
179 n->g2 = NAN;
180
181 return 0;
182 }
183 return 1;
184}
185
186static inline void _temp_params_from_array(dt_iop_temperature_params_t *p, const double a[4])
187{
188 p->red = a[0]; p->green = a[1]; p->blue = a[2]; p->g2 = a[3];
189}
190
191static inline void _temp_array_from_params(double a[4], const dt_iop_temperature_params_t *p)
192{
193 a[0] = p->red; a[1] = p->green; a[2] = p->blue; a[3] = p->g2;
194}
195
197{
198 // Ignore files that end with "-hdr.dng" since these are broken files we
199 // generated without any proper WB tagged
200 if(g_str_has_suffix(img->filename,"-hdr.dng"))
201 return TRUE;
202
203 static const char *const ignored_cameras[] = {
204 "Canon PowerShot A610",
205 "Canon PowerShot S3 IS",
206 "Canon PowerShot A620",
207 "Canon PowerShot A720 IS",
208 "Canon PowerShot A630",
209 "Canon PowerShot A640",
210 "Canon PowerShot A650",
211 "Canon PowerShot SX110 IS",
212 "Mamiya ZD",
213 "Canon EOS D2000C",
214 "Kodak EOS DCS 1",
215 "Kodak DCS560C",
216 "Kodak DCS460D",
217 "Nikon E5700",
218 "Sony DSC-F828",
219 "GITUP GIT2",
220 };
221
222 for(int i=0; i < sizeof(ignored_cameras)/sizeof(ignored_cameras[1]); i++)
223 if(!strcmp(img->camera_makermodel, ignored_cameras[i]))
224 return TRUE;
225
226 return FALSE;
227}
228
229
230const char *name()
231{
232 return C_("modulename", "white balance");
233}
234
235const char **description(struct dt_iop_module_t *self)
236{
237 return dt_iop_set_description(self, _("scale raw RGB channels to balance white and help demosaicing"),
238 _("corrective"),
239 _("linear, raw, scene-referred"),
240 _("linear, raw"),
241 _("linear, raw, scene-referred"));
242}
243
245{
246 return IOP_GROUP_TECHNICAL;
247}
248
253
255{
256 // This module may work in RAW or RGB (e.g. for TIFF files) depending on the input
257 // The module does not change the color space between the input and output, therefore implement it here
258 if(piece && piece->dsc_in.cst != IOP_CS_RAW)
259 return IOP_CS_RGB;
260 return IOP_CS_RAW;
261}
262
265{
266 default_output_format(self, pipe, piece, dsc);
267}
268
269/*
270 * Spectral power distribution functions
271 * https://en.wikipedia.org/wiki/Spectral_power_distribution
272 */
273typedef double((*spd)(unsigned long int wavelength, double TempK));
274
275/*
276 * Bruce Lindbloom, "Spectral Power Distribution of a Blackbody Radiator"
277 * http://www.brucelindbloom.com/Eqn_Blackbody.html
278 */
279static double spd_blackbody(unsigned long int wavelength, double TempK)
280{
281 // convert wavelength from nm to m
282 const long double lambda = (double)wavelength * 1e-9;
283
284/*
285 * these 2 constants were computed using following Sage code:
286 *
287 * (from http://physics.nist.gov/cgi-bin/cuu/Value?h)
288 * h = 6.62606957 * 10^-34 # Planck
289 * c= 299792458 # speed of light in vacuum
290 * k = 1.3806488 * 10^-23 # Boltzmann
291 *
292 * c_1 = 2 * pi * h * c^2
293 * c_2 = h * c / k
294 *
295 * print 'c_1 = ', c_1, ' ~= ', RealField(128)(c_1)
296 * print 'c_2 = ', c_2, ' ~= ', RealField(128)(c_2)
297 */
298
299#define c1 3.7417715246641281639549488324352159753e-16L
300#define c2 0.014387769599838156481252937624049081933L
301
302 return (double)(c1 / (powl(lambda, 5) * (expl(c2 / (lambda * TempK)) - 1.0L)));
303
304#undef c2
305#undef c1
306}
307
308/*
309 * Bruce Lindbloom, "Spectral Power Distribution of a CIE D-Illuminant"
310 * http://www.brucelindbloom.com/Eqn_DIlluminant.html
311 * and https://en.wikipedia.org/wiki/Standard_illuminant#Illuminant_series_D
312 */
313static double spd_daylight(unsigned long int wavelength, double TempK)
314{
315 cmsCIExyY WhitePoint = { 0.3127, 0.3290, 1.0 };
316
317 /*
318 * Bruce Lindbloom, "TempK to xy"
319 * http://www.brucelindbloom.com/Eqn_T_to_xy.html
320 */
321 cmsWhitePointFromTemp(&WhitePoint, TempK);
322
323 const double M = (0.0241 + 0.2562 * WhitePoint.x - 0.7341 * WhitePoint.y),
324 m1 = (-1.3515 - 1.7703 * WhitePoint.x + 5.9114 * WhitePoint.y) / M,
325 m2 = (0.0300 - 31.4424 * WhitePoint.x + 30.0717 * WhitePoint.y) / M;
326
327 const unsigned long int j
328 = ((wavelength - cie_daylight_components[0].wavelength)
329 / (cie_daylight_components[1].wavelength - cie_daylight_components[0].wavelength));
330
331 return (cie_daylight_components[j].S[0] + m1 * cie_daylight_components[j].S[1]
332 + m2 * cie_daylight_components[j].S[2]);
333}
334
335/*
336 * Bruce Lindbloom, "Computing XYZ From Spectral Data (Emissive Case)"
337 * http://www.brucelindbloom.com/Eqn_Spect_to_XYZ.html
338 */
339static cmsCIEXYZ spectrum_to_XYZ(double TempK, spd I)
340{
341 cmsCIEXYZ Source = {.X = 0.0, .Y = 0.0, .Z = 0.0 };
342
343 /*
344 * Color matching functions
345 * https://en.wikipedia.org/wiki/CIE_1931_color_space#Color_matching_functions
346 */
347 for(size_t i = 0; i < cie_1931_std_colorimetric_observer_count; i++)
348 {
349 const unsigned long int lambda = cie_1931_std_colorimetric_observer[0].wavelength
350 + (cie_1931_std_colorimetric_observer[1].wavelength
351 - cie_1931_std_colorimetric_observer[0].wavelength) * i;
352 const double P = I(lambda, TempK);
353 Source.X += P * cie_1931_std_colorimetric_observer[i].xyz.X;
354 Source.Y += P * cie_1931_std_colorimetric_observer[i].xyz.Y;
355 Source.Z += P * cie_1931_std_colorimetric_observer[i].xyz.Z;
356 }
357
358 // normalize so that each component is in [0.0, 1.0] range
359 const double _max = fmax(fmax(Source.X, Source.Y), Source.Z);
360 Source.X /= _max;
361 Source.Y /= _max;
362 Source.Z /= _max;
363
364 return Source;
365}
366
367// TODO: temperature and tint cannot be disjoined! (here it assumes no tint)
368static cmsCIEXYZ temperature_to_XYZ(double TempK)
369{
372
374 {
375 // if temperature is less than 4000K we use blackbody,
376 // because there will be no Daylight reference below 4000K...
377 return spectrum_to_XYZ(TempK, spd_blackbody);
378 }
379 else
380 {
381 return spectrum_to_XYZ(TempK, spd_daylight);
382 }
383}
384
385static cmsCIEXYZ temperature_tint_to_XYZ(double TempK, double tint)
386{
387 cmsCIEXYZ xyz = temperature_to_XYZ(TempK);
388
389 xyz.Y /= tint; // TODO: This is baaad!
390
391 return xyz;
392}
393
394// binary search inversion
395static void XYZ_to_temperature(cmsCIEXYZ XYZ, float *TempK, float *tint)
396{
397 double maxtemp = DT_IOP_HIGHEST_TEMPERATURE, mintemp = DT_IOP_LOWEST_TEMPERATURE;
398 cmsCIEXYZ _xyz;
399
400 for(*TempK = (maxtemp + mintemp) / 2.0; (maxtemp - mintemp) > 1.0; *TempK = (maxtemp + mintemp) / 2.0)
401 {
402 _xyz = temperature_to_XYZ(*TempK);
403 if(_xyz.Z / _xyz.X > XYZ.Z / XYZ.X)
404 maxtemp = *TempK;
405 else
406 mintemp = *TempK;
407 }
408
409 *tint = (_xyz.Y / _xyz.X) / (XYZ.Y / XYZ.X); // TODO: Fix this to move orthogonally to planckian locus
410
411
414 if(*tint < DT_IOP_LOWEST_TINT) *tint = DT_IOP_LOWEST_TINT;
415 if(*tint > DT_IOP_HIGHEST_TINT) *tint = DT_IOP_HIGHEST_TINT;
416}
417
418static void xyz2mul(dt_iop_module_t *self, cmsCIEXYZ xyz, double mul[4])
419{
421
422 double XYZ[3] = { xyz.X, xyz.Y, xyz.Z };
423
424 double CAM[4];
425 for(int k = 0; k < 4; k++)
426 {
427 CAM[k] = 0.0;
428 for(int i = 0; i < 3; i++)
429 {
430 CAM[k] += g->XYZ_to_CAM[k][i] * XYZ[i];
431 }
432 }
433
434 for(int k = 0; k < 4; k++) mul[k] = 1.0 / CAM[k];
435}
436
437static void temp2mul(dt_iop_module_t *self, double TempK, double tint, double mul[4])
438{
439 cmsCIEXYZ xyz = temperature_to_XYZ(TempK);
440
441 xyz.Y /= tint; // TODO: This is baaad!
450 xyz2mul(self, xyz, mul);
451}
452
454{
456
457 double CAM[4];
459 for(int k = 0; k < 4; k++) CAM[k] = CAM[k] > 0.0f ? 1.0 / CAM[k] : 0.0f;
460
461 double XYZ[3];
462 for(int k = 0; k < 3; k++)
463 {
464 XYZ[k] = 0.0;
465 for(int i = 0; i < 4; i++)
466 {
467 XYZ[k] += g->CAM_to_XYZ[k][i] * CAM[i];
468 }
469 }
470
471 return (cmsCIEXYZ){ XYZ[0], XYZ[1], XYZ[2] };
472}
473
474static void mul2temp(dt_iop_module_t *self, dt_iop_temperature_params_t *p, float *TempK, float *tint)
475{
476 XYZ_to_temperature(mul2xyz(self, p), TempK, tint);
477}
478
480int process(struct dt_iop_module_t *self, const dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece, const void *const ivoid,
481 void *const ovoid)
482{
483 const dt_iop_roi_t *const roi_out = &piece->roi_out;
484 const uint32_t filters = piece->dsc_in.filters;
485 const uint8_t(*const xtrans)[6] = (const uint8_t(*const)[6])piece->dsc_in.xtrans;
487 const int width = roi_out->width;
488 const int height = roi_out->height;
489
490 const float *const in = (const float *const)ivoid;
491 float *const out = (float *const)ovoid;
492 const float *const d_coeffs = d->coeffs;
493
494 if(filters == 9u)
495 { // xtrans float mosaiced
497 for(int j = 0; j < height; j++)
498 {
499 const size_t row_start = (size_t)j * width;
500 const float coeffs[12] =
501 {
502 d_coeffs[FCxtrans(j, 0, roi_out, xtrans)], d_coeffs[FCxtrans(j, 1, roi_out, xtrans)],
503 d_coeffs[FCxtrans(j, 2, roi_out, xtrans)], d_coeffs[FCxtrans(j, 3, roi_out, xtrans)],
504 d_coeffs[FCxtrans(j, 4, roi_out, xtrans)], d_coeffs[FCxtrans(j, 5, roi_out, xtrans)],
505 d_coeffs[FCxtrans(j, 6, roi_out, xtrans)], d_coeffs[FCxtrans(j, 7, roi_out, xtrans)],
506 d_coeffs[FCxtrans(j, 8, roi_out, xtrans)], d_coeffs[FCxtrans(j, 9, roi_out, xtrans)],
507 d_coeffs[FCxtrans(j, 10, roi_out, xtrans)], d_coeffs[FCxtrans(j, 11, roi_out, xtrans)],
508 };
509 /* Keep the 12-sensel X-Trans period explicit, but let the compiler pick
510 its own vectorization strategy on the scalar multiplies. */
511 int i = 0;
512 for(; i + 11 < width; i += 12)
513 {
514 const size_t p = row_start + i;
515 out[p + 0] = in[p + 0] * coeffs[0];
516 out[p + 1] = in[p + 1] * coeffs[1];
517 out[p + 2] = in[p + 2] * coeffs[2];
518 out[p + 3] = in[p + 3] * coeffs[3];
519 out[p + 4] = in[p + 4] * coeffs[4];
520 out[p + 5] = in[p + 5] * coeffs[5];
521 out[p + 6] = in[p + 6] * coeffs[6];
522 out[p + 7] = in[p + 7] * coeffs[7];
523 out[p + 8] = in[p + 8] * coeffs[8];
524 out[p + 9] = in[p + 9] * coeffs[9];
525 out[p + 10] = in[p + 10] * coeffs[10];
526 out[p + 11] = in[p + 11] * coeffs[11];
527 }
528 for(; i < width; i++)
529 {
530 const size_t p = row_start + i;
531 out[p] = in[p] * coeffs[i % 12];
532 }
533 }
534
535 }
536 else if(filters)
537 { // bayer float mosaiced
538 const int cfa_x = roi_out->x & 1;
540 for(int j = 0; j < height; j++)
541 {
542 const int offset_j = j + roi_out->y;
543 const size_t row_start = (size_t)j * width;
544 const int id0 = FC(offset_j, cfa_x + 0, filters);
545 const int id1 = FC(offset_j, cfa_x + 1, filters);
546 const float coeff0 = d_coeffs[id0];
547 const float coeff1 = d_coeffs[id1];
548 int i = 0;
549
550 for(; i + 1 < width; i += 2)
551 {
552 const size_t p = row_start + i;
553 out[p + 0] = in[p + 0] * coeff0;
554 out[p + 1] = in[p + 1] * coeff1;
555 }
556 for(; i < width; i++)
557 {
558 const size_t p = row_start + i;
559 out[p] = in[p] * d_coeffs[FC(offset_j, i + roi_out->x, filters)];
560 }
561 }
562
563 }
564 else
565 { // non-mosaiced
566 const size_t ch = piece->dsc_in.channels;
567 const size_t npixels = (size_t)roi_out->width * roi_out->height;
568
569 if(ch == 4)
570 {
572 for(size_t k = 0; k < npixels; k++)
573 {
574 const size_t p = 4 * k;
575 out[p + 0] = in[p + 0] * d->coeffs[0];
576 out[p + 1] = in[p + 1] * d->coeffs[1];
577 out[p + 2] = in[p + 2] * d->coeffs[2];
578 out[p + 3] = in[p + 3];
579 }
580
581 }
582 else
583 {
585 for(size_t k = 0; k < ch * npixels; k += ch)
586 {
587 for(ptrdiff_t c = 0; c < 3; c++)
588 {
589 const size_t p = k + c;
590 out[p] = in[p] * d->coeffs[c];
591 }
592 }
593
594 }
595
597 dt_iop_alpha_copy(ivoid, ovoid, roi_out->width, roi_out->height);
598 }
599
600 return 0;
601}
602
603#ifdef HAVE_OPENCL
604int process_cl(struct dt_iop_module_t *self, const dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece, cl_mem dev_in, cl_mem dev_out)
605{
606 const dt_iop_roi_t *const roi_in = &piece->roi_in;
607 const dt_iop_roi_t *const roi_out = &piece->roi_out;
610
611 const int devid = pipe->devid;
612 const uint32_t filters = piece->dsc_in.filters;
613 cl_mem dev_coeffs = NULL;
614 cl_mem dev_xtrans = NULL;
615 cl_int err = -999;
616 int kernel = -1;
617
618 if(filters == 9u)
619 {
621 }
622 else if(filters)
623 {
625 }
626 else
627 {
629 }
630
631 if(filters == 9u)
632 {
633 dev_xtrans = dt_opencl_copy_host_to_device_constant(devid, sizeof(piece->dsc_in.xtrans), (void *)piece->dsc_in.xtrans);
634 if(IS_NULL_PTR(dev_xtrans)) goto error;
635 }
636
637 dev_coeffs = dt_opencl_copy_host_to_device_constant(devid, sizeof(float) * 3, d->coeffs);
638 if(IS_NULL_PTR(dev_coeffs)) goto error;
639
640 const int width = roi_in->width;
641 const int height = roi_in->height;
642
643 size_t sizes[] = { ROUNDUPDWD(width, devid), ROUNDUPDHT(height, devid), 1 };
644 dt_opencl_set_kernel_arg(devid, kernel, 0, sizeof(cl_mem), (void *)&dev_in);
645 dt_opencl_set_kernel_arg(devid, kernel, 1, sizeof(cl_mem), (void *)&dev_out);
646 dt_opencl_set_kernel_arg(devid, kernel, 2, sizeof(int), (void *)&width);
647 dt_opencl_set_kernel_arg(devid, kernel, 3, sizeof(int), (void *)&height);
648 dt_opencl_set_kernel_arg(devid, kernel, 4, sizeof(cl_mem), (void *)&dev_coeffs);
649 dt_opencl_set_kernel_arg(devid, kernel, 5, sizeof(uint32_t), (void *)&filters);
650 dt_opencl_set_kernel_arg(devid, kernel, 6, sizeof(uint32_t), (void *)&roi_out->x);
651 dt_opencl_set_kernel_arg(devid, kernel, 7, sizeof(uint32_t), (void *)&roi_out->y);
652 dt_opencl_set_kernel_arg(devid, kernel, 8, sizeof(cl_mem), (void *)&dev_xtrans);
653 err = dt_opencl_enqueue_kernel_2d(devid, kernel, sizes);
654 if(err != CL_SUCCESS) goto error;
655
658
659 return TRUE;
660
661error:
664 dt_print(DT_DEBUG_OPENCL, "[opencl_white_balance] couldn't enqueue kernel! %d\n", err);
665 return FALSE;
666}
667#endif
668
669gboolean force_enable(struct dt_iop_module_t *self, const gboolean current_state)
670{
671 // History sanitization: white balance has nothing to balance on a true monochrome sensor, so a
672 // WB entry pasted onto a monochrome image is forced off here, at history-read time. For every
673 // other image (including non-raw, where manual WB is allowed) the user's state is preserved.
674 // This mirrors reload_defaults(), which hides the on/off button for monochrome images.
675 const gboolean mono = dt_image_is_monochrome(&self->dev->image_storage);
676 const gboolean state = current_state && !mono;
677 dt_iop_fmt_log(self, "force_enable: class=%s mono=%d current=%d -> %d",
679 mono, current_state, state);
680 return state;
681}
682
683// Publish the given WB multipliers into dev->proxy.wb_coeffs, the dev-wide value consumed by
684// channelmixerrgb's chromatic adaptation (get_white_balance_coeff()). dev->proxy is a GUI/main-thread
685// channel for inter-module communication; it MUST NOT be accessed from the pixel pipeline (neither
686// read nor write -- the pipeline only needs module params). It is published purely on the main
687// thread: reload_defaults() seeds the per-image default, commit_proxy() refreshes it once
688// history/presets are applied, and every GUI history commit re-publishes it (see
689// dt_dev_add_history_item_real / dt_dev_pop_history_items_ext).
690// (NOTE: channelmixerrgb still reads this from its process()/commit_params() for the live
691// DT_ILLUMINANT_CAMERA tracking -- that is a remaining violation of the rule, to be removed by
692// resolving the camera illuminant on the main thread into its params.)
694{
695 if(IS_NULL_PTR(self->dev)) return;
696 // Mirror the legacy commit_params() values exactly: g2 falls back to the first green when it is
697 // not a usable number on a non-4-colour sensor (NaN/0 would poison the G2 CFA sites downstream).
698 const gboolean g2_usable = isnormal(p->g2) || (self->dev->image_storage.flags & DT_IMAGE_4BAYER);
699 self->dev->proxy.wb_coeffs[0] = p->red;
700 self->dev->proxy.wb_coeffs[1] = p->green;
701 self->dev->proxy.wb_coeffs[2] = p->blue;
702 self->dev->proxy.wb_coeffs[3] = g2_usable ? p->g2 : p->green;
703}
704
705// Main-thread proxy commit, invoked by the history layer after params are applied (and before any
706// pipeline runs). Publishes the module's EFFECTIVE (post-history/preset) WB to dev->proxy.
711
714{
718
719 d->coeffs[0] = p->red;
720 d->coeffs[1] = p->green;
721 d->coeffs[2] = p->blue;
722 // Legacy-history / non-4-colour safety net: older edits may have stored g2 = NaN (the historical
723 // "usually NAN for RGB" second-green value, see find_coeffs). A NaN/0 multiplier would poison
724 // half the green CFA sites, so fall back to the first green when g2 isn't a usable number.
725 const gboolean g2_usable = isnormal(p->g2) || (self->dev->image_storage.flags & DT_IMAGE_4BAYER);
726 d->coeffs[3] = g2_usable ? p->g2 : p->green;
727 dt_iop_fmt_log(self, "commit: class=%s matrix_supported=%d coeffs=[%.4f %.4f %.4f %.4f] g2_in=%.4f g2_usable=%d -> enabled=%d",
730 d->coeffs[0], d->coeffs[1], d->coeffs[2], d->coeffs[3], p->g2, g2_usable, piece->enabled);
731 // NOTE: dev->proxy.wb_coeffs is intentionally NOT written here. dev->proxy is a GUI/main-thread
732 // inter-module channel and must not be touched from the pipeline; writing it from this
733 // pipeline-thread callback was a data race. It is now published on the main thread via
734 // commit_proxy() / reload_defaults().
735 piece->dsc_out.temperature.enabled = 1;
736 for(int k = 0; k < 4; k++)
737 {
738 piece->dsc_out.temperature.coeffs[k] = d->coeffs[k];
739 piece->dsc_out.processed_maximum[k] = piece->dsc_in.processed_maximum[k] * d->coeffs[k];
740 }
741
742 // 4Bayer images not implemented in OpenCL yet
744
745 if(!IS_NULL_PTR(g))
746 {
747 // advertise on the pipe if coeffs are D65 for validity check
748 gboolean is_D65 = TRUE;
749 for(int c = 0; c < 3; c++)
750 if(d->coeffs[c] != (float)g->daylight_wb[c]) is_D65 = FALSE;
751
752 self->dev->proxy.wb_is_D65 = is_D65;
753 }
754}
755
757{
759 piece->data_size = sizeof(dt_iop_temperature_data_t);
760}
761
763{
764 dt_free_align(piece->data);
765 piece->data = NULL;
766}
767
769{
771
772 const gboolean color_rgb = g->colored_sliders &&
774
779 dt_bauhaus_slider_set_feedback(g->scale_r, !color_rgb);
780 dt_bauhaus_slider_set_feedback(g->scale_g, !color_rgb);
781 dt_bauhaus_slider_set_feedback(g->scale_b, !color_rgb);
782 dt_bauhaus_slider_set_feedback(g->scale_g2, !color_rgb);
783
784 if(!color_rgb) return;
785
786 // there are 3 ways to do colored sliders: naive (independent 0->1), smart(er) (dependent 0->1) and real (coeff)
787
788 if(FALSE)
789 {
790 //naive:
791 dt_bauhaus_slider_set_stop(g->scale_r, 0.0, 0.0, 0.0, 0.0);
792 dt_bauhaus_slider_set_stop(g->scale_r, 1.0, 1.0, 0.0, 0.0);
793
794 dt_bauhaus_slider_set_stop(g->scale_g, 0.0, 0.0, 0.0, 0.0);
795 dt_bauhaus_slider_set_stop(g->scale_g, 1.0, 0.0, 1.0, 0.0);
796
797 dt_bauhaus_slider_set_stop(g->scale_b, 0.0, 0.0, 0.0, 0.0);
798 dt_bauhaus_slider_set_stop(g->scale_b, 1.0, 0.0, 0.0, 1.0);
799
800 dt_bauhaus_slider_set_stop(g->scale_g2, 0.0, 0.0, 0.0, 0.0);
801 dt_bauhaus_slider_set_stop(g->scale_g2, 1.0, 0.0, 1.0, 0.0);
802 }
803 if(!g->blackbody_is_confusing)
804 {
805 //smart(er) than naive
806 const float rchan = dt_bauhaus_slider_get(g->scale_r) / dt_bauhaus_slider_get_hard_max(g->scale_r);
807 const float gchan = dt_bauhaus_slider_get(g->scale_g) / dt_bauhaus_slider_get_hard_max(g->scale_g);
808 const float bchan = dt_bauhaus_slider_get(g->scale_b) / dt_bauhaus_slider_get_hard_max(g->scale_b);
809
810 dt_bauhaus_slider_set_stop(g->scale_r, 0.0, 0.0, gchan, bchan);
811 dt_bauhaus_slider_set_stop(g->scale_r, 1.0, 1.0, gchan, bchan);
812
813 dt_bauhaus_slider_set_stop(g->scale_g, 0.0, rchan, 0.0, bchan);
814 dt_bauhaus_slider_set_stop(g->scale_g, 1.0, rchan, 1.0, bchan);
815
816 dt_bauhaus_slider_set_stop(g->scale_b, 0.0, rchan, gchan, 0.0);
817 dt_bauhaus_slider_set_stop(g->scale_b, 1.0, rchan, gchan, 1.0);
818 }
819 else
820 {
821 //real (ish)
822 //we consider daylight wb to be "reference white"
823 const double white[3] = {
824 1.0/g->daylight_wb[0],
825 1.0/g->daylight_wb[1],
826 1.0/g->daylight_wb[2],
827 };
828
829 const float rchanmul = dt_bauhaus_slider_get(g->scale_r);
830 const float rchanmulmax = dt_bauhaus_slider_get_hard_max(g->scale_r);
831 const float gchanmul = dt_bauhaus_slider_get(g->scale_g);
832 const float gchanmulmax = dt_bauhaus_slider_get_hard_max(g->scale_g);
833 const float bchanmul = dt_bauhaus_slider_get(g->scale_b);
834 const float bchanmulmax = dt_bauhaus_slider_get_hard_max(g->scale_g);
835
836 dt_bauhaus_slider_set_stop(g->scale_r, 0.0, white[0]*0.0, white[1]*gchanmul, white[2]*bchanmul);
837 dt_bauhaus_slider_set_stop(g->scale_r, g->daylight_wb[0]/rchanmulmax, white[0]*g->daylight_wb[0], white[1]*gchanmul, white[2]*bchanmul);
838 dt_bauhaus_slider_set_stop(g->scale_r, 1.0, white[0]*1.0, white[1]*(gchanmul/gchanmulmax), white[2]*(bchanmul/bchanmulmax));
839
840 dt_bauhaus_slider_set_stop(g->scale_g, 0.0, white[0]*rchanmul, white[1]*0.0, white[2]*bchanmul);
841 dt_bauhaus_slider_set_stop(g->scale_g, g->daylight_wb[1]/bchanmulmax, white[0]*rchanmul, white[1]*g->daylight_wb[1], white[2]*bchanmul);
842 dt_bauhaus_slider_set_stop(g->scale_g, 1.0, white[0]*(rchanmul/rchanmulmax), white[1]*1.0, white[2]*(bchanmul/bchanmulmax));
843
844 dt_bauhaus_slider_set_stop(g->scale_b, 0.0, white[0]*rchanmul, white[1]*gchanmul, white[2]*0.0);
845 dt_bauhaus_slider_set_stop(g->scale_b, g->daylight_wb[2]/bchanmulmax, white[0]*rchanmul, white[1]*gchanmul, white[2]*g->daylight_wb[2]);
846 dt_bauhaus_slider_set_stop(g->scale_b, 1.0, white[0]*(rchanmul/rchanmulmax), white[1]*(gchanmul/gchanmulmax), white[2]*1.0);
847 }
848
849 if(gtk_widget_get_visible(GTK_WIDGET(g->scale_r)))
850 {
851 gtk_widget_queue_draw(GTK_WIDGET(g->scale_r));
852 gtk_widget_queue_draw(GTK_WIDGET(g->scale_g));
853 gtk_widget_queue_draw(GTK_WIDGET(g->scale_b));
854 }
855}
856
858{
860
862 dt_bauhaus_slider_clear_stops(g->scale_tint);
863 dt_bauhaus_slider_set_feedback(g->scale_k, !g->colored_sliders);
864 dt_bauhaus_slider_set_feedback(g->scale_tint, !g->colored_sliders);
865
866 if(!g->colored_sliders) return;
867
869 const double tint_step = (double)(DT_IOP_HIGHEST_TINT - DT_IOP_LOWEST_TINT) / (DT_BAUHAUS_SLIDER_MAX_STOPS - 1.0);
870 const int blackbody_is_confusing = g->blackbody_is_confusing;
871
872 const float cur_temp = dt_bauhaus_slider_get(g->scale_k);
873 const float cur_tint = dt_bauhaus_slider_get(g->scale_tint);
874
875 //we consider daylight wb to be "reference white"
876 const double dayligh_white[3] = {
877 1.0/g->daylight_wb[0],
878 1.0/g->daylight_wb[1],
879 1.0/g->daylight_wb[2],
880 };
881
882 double cur_coeffs[4] = {0.0};
883 temp2mul(self, cur_temp, 1.0, cur_coeffs);
884 const double cur_white[3] = {
885 1.0/cur_coeffs[0],
886 1.0/cur_coeffs[1],
887 1.0/cur_coeffs[2],
888 };
889
890 if(blackbody_is_confusing)
891 {
892 // show effect of adjustment on temp/tint sliders
893 for(int i = 0; i < DT_BAUHAUS_SLIDER_MAX_STOPS; i++)
894 {
895 const float stop = i / (DT_BAUHAUS_SLIDER_MAX_STOPS - 1.0);
896 const double K = DT_IOP_LOWEST_TEMPERATURE + i * temp_step;
897 const double tint = DT_IOP_LOWEST_TINT + i * tint_step;
898
899 double coeffs_K[4];
900 double coeffs_tint[4];
901 temp2mul(self, K, cur_tint, coeffs_K);
902 temp2mul(self, cur_temp, tint, coeffs_tint);
903 coeffs_K[0] /= coeffs_K[1];
904 coeffs_K[2] /= coeffs_K[1];
905 coeffs_K[3] /= coeffs_K[1];
906 coeffs_K[1] = 1.0;
907 coeffs_tint[0] /= coeffs_tint[1];
908 coeffs_tint[2] /= coeffs_tint[1];
909 coeffs_tint[3] /= coeffs_tint[1];
910 coeffs_tint[1] = 1.0;
911
912 dt_aligned_pixel_t sRGB_K = { dayligh_white[0]*coeffs_K[0], dayligh_white[1]*coeffs_K[1],
913 dayligh_white[2]*coeffs_K[2] };
914 dt_aligned_pixel_t sRGB_tint = {cur_white[0]*coeffs_tint[0], cur_white[1]*coeffs_tint[1],
915 cur_white[2]*coeffs_tint[2]};
916 const float maxsRGB_K = fmaxf(fmaxf(sRGB_K[0], sRGB_K[1]), sRGB_K[2]);
917 const float maxsRGB_tint = fmaxf(fmaxf(sRGB_tint[0], sRGB_tint[1]),sRGB_tint[2]);
918
919 if(maxsRGB_K > 1.f)
920 {
921 for(int ch=0; ch<3; ch++)
922 {
923 sRGB_K[ch] = fmaxf(sRGB_K[ch] / maxsRGB_K, 0.f);
924 }
925 }
926 if(maxsRGB_tint > 1.f)
927 {
928 for(int ch=0; ch<3; ch++)
929 {
930 sRGB_tint[ch] = fmaxf(sRGB_tint[ch] / maxsRGB_tint, 0.f);
931 }
932 }
933 dt_bauhaus_slider_set_stop(g->scale_k, stop, sRGB_K[0], sRGB_K[1], sRGB_K[2]);
934 dt_bauhaus_slider_set_stop(g->scale_tint, stop, sRGB_tint[0], sRGB_tint[1], sRGB_tint[2]);
935 }
936 }
937 else
938 {
939 // reflect actual black body colors for the temperature slider
940 for(int i = 0; i < DT_BAUHAUS_SLIDER_MAX_STOPS; i++)
941 {
942 const float stop = i / (DT_BAUHAUS_SLIDER_MAX_STOPS - 1.0);
943 const double K = DT_IOP_LOWEST_TEMPERATURE + i * temp_step;
944 const double tint = DT_IOP_LOWEST_TINT + i * tint_step;
945
946 const cmsCIEXYZ cmsXYZ_temp = temperature_tint_to_XYZ(K,cur_tint);
947 const cmsCIEXYZ cmsXYZ_tint = temperature_tint_to_XYZ(cur_temp, tint);
948 dt_aligned_pixel_t XYZ_temp = {cmsXYZ_temp.X, cmsXYZ_temp.Y, cmsXYZ_temp.Z};
949 dt_aligned_pixel_t XYZ_tint = {cmsXYZ_tint.X, cmsXYZ_tint.Y, cmsXYZ_tint.Z};
950 dt_aligned_pixel_t sRGB_temp;
951 dt_aligned_pixel_t sRGB_tint;
952
953 dt_XYZ_to_Rec709_D65(XYZ_temp, sRGB_temp);
954 dt_XYZ_to_Rec709_D65(XYZ_tint, sRGB_tint);
955
956 const float maxsRGB_temp = fmaxf(fmaxf(sRGB_temp[0], sRGB_temp[1]), sRGB_temp[2]);
957 const float maxsRGB_tint = fmaxf(fmaxf(sRGB_tint[0], sRGB_tint[1]), sRGB_tint[2]);
958
959 if(maxsRGB_temp > 1.f)
960 {
961 for(int ch=0; ch<3; ch++)
962 {
963 sRGB_temp[ch] = fmaxf(sRGB_temp[ch] / maxsRGB_temp, 0.f);
964 }
965 }
966
967 if(maxsRGB_tint > 1.f)
968 {
969 for(int ch=0; ch<3; ch++)
970 {
971 sRGB_tint[ch] = fmaxf(sRGB_tint[ch] / maxsRGB_tint, 0.f);
972 }
973 }
974
975 dt_bauhaus_slider_set_stop(g->scale_k, stop, sRGB_temp[0], sRGB_temp[1], sRGB_temp[2]);
976 dt_bauhaus_slider_set_stop(g->scale_tint, stop, sRGB_tint[0], sRGB_tint[1], sRGB_tint[2]);
977 }
978 }
979
980 if(gtk_widget_get_visible(GTK_WIDGET(g->scale_k)))
981 {
982 gtk_widget_queue_draw(GTK_WIDGET(g->scale_k));
983 gtk_widget_queue_draw(GTK_WIDGET(g->scale_tint));
984 }
985}
986
987
988void gui_update(struct dt_iop_module_t *self)
989{
992
993 const gboolean monochrome = dt_image_is_monochrome(&self->dev->image_storage);
994 const gboolean is_raw = dt_image_is_matrix_correction_supported(&self->dev->image_storage);
995 self->hide_enable_button = monochrome;
996 self->default_enabled = is_raw;
997 gtk_stack_set_visible_child_name(GTK_STACK(self->widget), self->hide_enable_button ? "disabled" : "enabled");
998
999// if(self->hide_enable_button) return;
1000
1001 // Per-image WB references + widget defaults, formerly computed in reload_defaults(). These need
1002 // live widgets (bauhaus slider defaults, the presets combobox) and the GUI conversion matrices,
1003 // so they belong here on the GUI thread. Read the already-computed default_params; never recompute.
1005
1006 prepare_matrices(self);
1007
1008 dt_bauhaus_slider_set_default(g->scale_r, d->red);
1009 dt_bauhaus_slider_set_default(g->scale_g, d->green);
1010 dt_bauhaus_slider_set_default(g->scale_b, d->blue);
1011 dt_bauhaus_slider_set_default(g->scale_g2, d->g2);
1012
1013 // remember daylight wb used for temperature/tint conversion, assuming it corresponds to CIE D65
1014 _temp_array_from_params(g->daylight_wb, d);
1015 calculate_bogus_daylight_wb(self, g->daylight_wb);
1016
1017 // Store EXIF WB coeffs
1018 if(is_raw)
1019 find_coeffs(self, g->as_shot_wb);
1020 else
1021 g->as_shot_wb[0] = g->as_shot_wb[1] = g->as_shot_wb[2] = g->as_shot_wb[3] = 1.f;
1022
1023 g->as_shot_wb[0] /= g->as_shot_wb[1];
1024 g->as_shot_wb[2] /= g->as_shot_wb[1];
1025 g->as_shot_wb[3] /= g->as_shot_wb[1];
1026 g->as_shot_wb[1] = 1.0;
1027
1028 {
1029 float TempK_def, tint_def;
1030 mul2temp(self, d, &TempK_def, &tint_def);
1031 dt_bauhaus_slider_set_default(g->scale_k, TempK_def);
1032 dt_bauhaus_slider_set_default(g->scale_tint, tint_def);
1033 }
1034
1035 dt_bauhaus_combobox_clear(g->presets);
1036 dt_bauhaus_combobox_add(g->presets, C_("white balance", "as shot")); // old "camera". reason for change: all other RAW development tools use "As Shot" or "shot"
1037 dt_bauhaus_combobox_add(g->presets, C_("white balance", "from image area")); // old "spot", reason: describes exactly what'll happen
1038 dt_bauhaus_combobox_add(g->presets, C_("white balance", "user modified"));
1039 dt_bauhaus_combobox_add(g->presets, C_("white balance", "camera reference")); // old "camera neutral", reason: better matches intent
1040 g->preset_cnt = DT_IOP_NUM_OF_STD_TEMP_PRESETS;
1041 memset(g->preset_num, 0, sizeof(g->preset_num));
1042
1043 gui_sliders_update(self);
1044
1046
1047 float tempK, tint;
1048 mul2temp(self, p, &tempK, &tint);
1049
1050 dt_bauhaus_slider_set(g->scale_k, tempK);
1051 dt_bauhaus_slider_set(g->scale_tint, tint);
1052 dt_bauhaus_slider_set(g->scale_r, p->red);
1053 dt_bauhaus_slider_set(g->scale_g, p->green);
1054 dt_bauhaus_slider_set(g->scale_b, p->blue);
1055 dt_bauhaus_slider_set(g->scale_g2, p->g2);
1056
1057 dt_bauhaus_combobox_set(g->presets, -1);
1058
1059 gboolean found = FALSE;
1060
1061 // is this a "as shot" white balance?
1062 if(p->red == g->as_shot_wb[0] && p->green == g->as_shot_wb[1] && p->blue == g->as_shot_wb[2])
1063 {
1065 found = TRUE;
1066 }
1067 else
1068 {
1069 // is this a "D65 white balance"?
1070 if((p->red == (float)g->daylight_wb[0]) &&
1071 (p->green == (float)g->daylight_wb[1]) &&
1072 (p->blue == (float)g->daylight_wb[2]))
1073 {
1075 found = TRUE;
1076 }
1077 }
1078
1079 if(!found)
1081
1082 if (!found || isnan(g->mod_temp)) // reset or initialize user-defined
1083 {
1084 g->mod_temp = tempK;
1085 g->mod_tint = tint;
1086 _temp_array_from_params(g->mod_coeff, p);
1087 }
1088
1089 gtk_widget_set_visible(g->buttonbar, g->button_bar_visible);
1090
1091 const int preset = dt_bauhaus_combobox_get(g->presets);
1092
1093 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(g->btn_asshot), preset == DT_IOP_TEMP_AS_SHOT);
1094 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(g->btn_user), preset == DT_IOP_TEMP_USER);
1095 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(g->btn_d65), preset == DT_IOP_TEMP_D65);
1096
1098 color_rgb_sliders(self);
1099
1101
1102 gtk_widget_queue_draw(self->widget);
1103}
1104
1105static int calculate_bogus_daylight_wb(dt_iop_module_t *module, double bwb[4])
1106{
1108 {
1109 bwb[0] = 1.0;
1110 bwb[2] = 1.0;
1111 bwb[1] = 1.0;
1112 bwb[3] = 1.0;
1113
1114 return 0;
1115 }
1116
1117 double mul[4];
1119 NULL, NULL,
1120 module->dev->image_storage.d65_color_matrix, mul))
1121 {
1122 // normalize green:
1123 bwb[0] = mul[0] / mul[1];
1124 bwb[2] = mul[2] / mul[1];
1125 bwb[1] = 1.0;
1126 bwb[3] = mul[3] / mul[1];
1127
1128 return 0;
1129 }
1130
1131 return 1;
1132}
1133
1135{
1137
1138 // sRGB D65
1139 const double RGB_to_XYZ[3][4] = { { 0.4124564, 0.3575761, 0.1804375, 0 },
1140 { 0.2126729, 0.7151522, 0.0721750, 0 },
1141 { 0.0193339, 0.1191920, 0.9503041, 0 } };
1142
1143 // sRGB D65
1144 const double XYZ_to_RGB[4][3] = { { 3.2404542, -1.5371385, -0.4985314 },
1145 { -0.9692660, 1.8760108, 0.0415560 },
1146 { 0.0556434, -0.2040259, 1.0572252 },
1147 { 0, 0, 0 } };
1148
1150 {
1151 // let's just assume for now(TM) that if it has no raw colorimetry, it is sRGB.
1152 // Gate on the colorimetry axis (raw or sRAW), not on the mosaic-only "raw" flag, otherwise
1153 // an already-demosaiced raw (sRAW / linear DNG) would skip its camera matrix here (issue #729).
1154 memcpy(g->XYZ_to_CAM, XYZ_to_RGB, sizeof(g->XYZ_to_CAM));
1155 memcpy(g->CAM_to_XYZ, RGB_to_XYZ, sizeof(g->CAM_to_XYZ));
1156 return;
1157 }
1158
1161 g->XYZ_to_CAM, g->CAM_to_XYZ))
1162 {
1163 char *camera = module->dev->image_storage.camera_makermodel;
1164 fprintf(stderr, "[temperature] `%s' color matrix not found for image\n", camera);
1165 dt_control_log(_("`%s' color matrix not found for image"), camera);
1166 }
1167}
1168
1169static void find_coeffs(dt_iop_module_t *module, double coeffs[4])
1170{
1171 const dt_image_t *img = &module->dev->image_storage;
1172
1173 // the raw should provide wb coeffs:
1174 int ok = 1;
1175 // Only check the first three values, the fourth is usually NAN for RGB
1176 const int num_coeffs = (img->flags & DT_IMAGE_4BAYER) ? 4 : 3;
1177 for(int k = 0; ok && k < num_coeffs; k++)
1178 {
1179 if(!isnormal(img->wb_coeffs[k]) || img->wb_coeffs[k] == 0.0f) ok = 0;
1180 }
1181 if(ok)
1182 {
1183 for(int k = 0; k < 4; k++) coeffs[k] = img->wb_coeffs[k];
1184 // wb_coeffs[3] is the SECOND green multiplier. For non-4-colour sensors (standard RGGB Bayer,
1185 // X-Trans) both green sites physically share the first green's multiplier, and many decoders
1186 // legitimately leave wb_coeffs[3] as NaN or 0 (see the "usually NAN for RGB" note above, and
1187 // the rawspeed DngDecoder which only fills 3 planes). Mirror coeffs[1] when the fourth value
1188 // is not a usable number, so we never propagate NaN into the G2 sites of the CFA.
1189 if(!(img->flags & DT_IMAGE_4BAYER) && !isnormal(coeffs[3])) coeffs[3] = coeffs[1];
1190 return;
1191 }
1192
1193 if(!ignore_missing_wb(&(module->dev->image_storage)))
1194 {
1195 // only display this if we have a sample, otherwise it is better to keep
1196 // on screen the more important message about missing sample and the way
1197 // to contribute.
1198 if(!img->camera_missing_sample)
1199 dt_control_log(_("failed to read camera white balance information from `%s'!"),
1200 img->filename);
1201 fprintf(stderr, "[temperature] failed to read camera white balance information from `%s'!\n",
1202 img->filename);
1203 }
1204
1205 double bwb[4];
1206 if(!calculate_bogus_daylight_wb(module, bwb))
1207 {
1208 // found camera matrix and used it to calculate bogus daylight wb
1209 for(int c = 0; c < 4; c++) coeffs[c] = bwb[c];
1210 return;
1211 }
1212
1213 // did not find preset either?
1214 // final security net: hardcoded default that fits most cams.
1215 coeffs[0] = 2.0;
1216 coeffs[1] = 1.0;
1217 coeffs[2] = 1.5;
1218 coeffs[3] = 1.0;
1219}
1220
1222{
1223 dt_iop_temperature_params_t *d = module->default_params;
1224 *d = (dt_iop_temperature_params_t){ 1.0, 1.0, 1.0, 1.0 };
1225
1226 // we might be called from presets update infrastructure => there is no image
1227 if(!module->dev || module->dev->image_storage.id == -1) return;
1228
1229 const gboolean is_raw = dt_image_is_matrix_correction_supported(&module->dev->image_storage);
1230 const gboolean monochrome = dt_image_is_monochrome(&module->dev->image_storage);
1231
1232 // Note : this is not an user param anymore, and is set to modern by default
1233 // except for old histories using temperature without having an history entry for it.
1234 // see develop/develop.c/_dev_auto_apply_presets()
1235 const gboolean is_modern =
1236 dt_conf_is_equal("plugins/darkroom/chromatic-adaptation", "modern");
1237
1238 module->default_enabled = 0;
1239 module->hide_enable_button = 0;
1240
1241 // White balance module doesn't need to be enabled for monochrome raws (like
1242 // for leica monochrom cameras). prepare_matrices is a noop as well, as there
1243 // isn't a color matrix, so we can skip that as well.
1244 if(monochrome)
1245 {
1246 module->hide_enable_button = 1;
1247 }
1248 else
1249 {
1250 /* check if file is raw / hdr */
1251 if(is_raw)
1252 {
1253 // raw images need wb:
1254 module->default_enabled = 1;
1255
1256 // if workflow = modern, only set WB coeffs equivalent to D65 illuminant
1257 // full chromatic adaptation is deferred to channelmixerrgb
1258 double coeffs[4] = { 0 };
1259 if(is_modern && !calculate_bogus_daylight_wb(module, coeffs))
1260 {
1261 d->red = coeffs[0]/coeffs[1];
1262 d->blue = coeffs[2]/coeffs[1];
1263 d->g2 = coeffs[3]/coeffs[1];
1264 d->green = 1.0f;
1265 }
1266 else
1267 {
1268 // do best to find starting coeffs
1269 find_coeffs(module, coeffs);
1270 d->red = coeffs[0]/coeffs[1];
1271 d->blue = coeffs[2]/coeffs[1];
1272 d->g2 = coeffs[3]/coeffs[1];
1273 d->green = 1.0f;
1274 }
1275 }
1276 }
1277
1278 dt_iop_fmt_log(module, "reload_defaults: class=%s matrix_supported=%d mono=%d modern=%d -> default_enabled=%d coeffs=[%.4f %.4f %.4f %.4f]",
1280 is_raw, monochrome, is_modern, module->default_enabled, d->red, d->green, d->blue, d->g2);
1281
1282 // Seed the dev WB proxy with the per-image DEFAULT coeffs (main thread). channelmixerrgb derives
1283 // its own per-image defaults from dev->proxy.wb_coeffs, and reload_defaults() runs in iop order
1284 // (temperature before channelmixerrgb) at history init, so this baseline is visible to it. The
1285 // EFFECTIVE (history/preset) coeffs are published later by commit_proxy() once history is applied.
1286 // KNOWN GAP (no fix landed yet): on a user WB auto-preset, channelmixerrgb's default is derived
1287 // here against this pre-preset baseline, so a fresh init does not match a later manual reset.
1288 _publish_wb_coeffs(module, d);
1289
1290 // NOTE: widget state (slider/combo defaults, the daylight/as-shot WB references, the presets
1291 // combobox, the per-CFA slider labels) is intentionally NOT set here. reload_defaults() runs on
1292 // export/thumbnail devs with no widgets and off the GUI thread; touching bauhaus here re-entered
1293 // callbacks against half-built widgets and crashed. That work now lives in gui_update(), reading
1294 // these same default_params.
1295}
1296
1298{
1299 const int program = 2; // basic.cl, from programs.conf
1302 module->data = gd;
1303 gd->kernel_whitebalance_4f = dt_opencl_create_kernel(program, "whitebalance_4f");
1304 gd->kernel_whitebalance_1f = dt_opencl_create_kernel(program, "whitebalance_1f");
1305 gd->kernel_whitebalance_1f_xtrans = dt_opencl_create_kernel(program, "whitebalance_1f_xtrans");
1306}
1307
1316
1318{
1319 if(dt_gui_widgets_suppressed()) return;
1320
1323
1325
1326 g->mod_temp = dt_bauhaus_slider_get(g->scale_k);
1327 g->mod_tint = dt_bauhaus_slider_get(g->scale_tint);
1328
1329 temp2mul(self, g->mod_temp, g->mod_tint, g->mod_coeff);
1330
1331 // normalize
1332 g->mod_coeff[0] /= g->mod_coeff[1];
1333 g->mod_coeff[2] /= g->mod_coeff[1];
1334 g->mod_coeff[3] /= g->mod_coeff[1];
1335 g->mod_coeff[1] = 1.0;
1336
1337 _temp_params_from_array(p, g->mod_coeff);
1338
1340 dt_bauhaus_slider_set(g->scale_r, p->red);
1341 dt_bauhaus_slider_set(g->scale_g, p->green);
1342 dt_bauhaus_slider_set(g->scale_b, p->blue);
1343 dt_bauhaus_slider_set(g->scale_g2, p->g2);
1346
1348}
1349
1350void gui_changed(dt_iop_module_t *self, GtkWidget *w, void *previous)
1351{
1354
1355 _temp_array_from_params(g->mod_coeff, p);
1356
1357 mul2temp(self, p, &g->mod_temp, &g->mod_tint);
1358
1360}
1361
1362static gboolean btn_toggled(GtkWidget *togglebutton, GdkEventButton *event, dt_iop_module_t *self)
1363{
1364 if(dt_gui_widgets_suppressed()) return TRUE;
1365
1367
1368 int preset = togglebutton == g->btn_asshot ? DT_IOP_TEMP_AS_SHOT :
1369 togglebutton == g->btn_d65 ? DT_IOP_TEMP_D65 :
1370 togglebutton == g->btn_user ? DT_IOP_TEMP_USER : 0;
1371
1372 if(!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(togglebutton)))
1373 {
1374 if(dt_bauhaus_combobox_get(g->presets) != preset)
1375 dt_bauhaus_combobox_set(g->presets, preset);
1376 }
1377 else if(dt_bauhaus_combobox_get(g->presets) == preset)
1378 {
1379 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(togglebutton), TRUE);
1380 }
1381
1382 return TRUE;
1383}
1384
1386{
1387 if(dt_gui_widgets_suppressed()) return;
1388
1391
1392 const int pos = dt_bauhaus_combobox_get(g->presets);
1393
1394 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(g->btn_asshot), pos == DT_IOP_TEMP_AS_SHOT);
1396 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(g->btn_user), pos == DT_IOP_TEMP_USER);
1397 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(g->btn_d65), pos == DT_IOP_TEMP_D65);
1398
1399 switch(pos)
1400 {
1401 case -1: // just un-setting.
1402 return;
1403 case DT_IOP_TEMP_AS_SHOT: // as shot wb
1404 _temp_params_from_array(p, g->as_shot_wb);
1405 break;
1406 case DT_IOP_TEMP_SPOT: // from image area wb, expose callback will set p->rgbg2.
1407 if(!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(g->colorpicker)))
1408 {
1409 gboolean ret_val;
1410 g_signal_emit_by_name(G_OBJECT(g->colorpicker), "button-press-event", NULL, &ret_val);
1411 }
1412 break;
1413 case DT_IOP_TEMP_USER: // directly changing one of the coeff sliders also changes the mod_coeff so it can be read here
1414 _temp_params_from_array(p, g->mod_coeff);
1415 break;
1416 case DT_IOP_TEMP_D65: // camera reference d65
1417 _temp_params_from_array(p, g->daylight_wb);
1418 break;
1419 default: // camera WB presets
1420 break;
1421 }
1422
1423 if(self->off) gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(self->off), 1);
1424
1425 float TempK, tint;
1426
1427 if(pos == DT_IOP_TEMP_USER)
1428 {
1429 TempK = g->mod_temp;
1430 tint = g->mod_tint;
1431 }
1432 else
1433 {
1434 mul2temp(self, p, &TempK, &tint);
1435 }
1436
1438 dt_bauhaus_slider_set(g->scale_k, TempK);
1439 dt_bauhaus_slider_set(g->scale_tint, tint);
1440 dt_bauhaus_slider_set(g->scale_r, p->red);
1441 dt_bauhaus_slider_set(g->scale_g, p->green);
1442 dt_bauhaus_slider_set(g->scale_b, p->blue);
1443 dt_bauhaus_slider_set(g->scale_g2, p->g2);
1445
1447 color_rgb_sliders(self);
1448
1450}
1451
1453{
1454 (void)picker;
1455 (void)pipe;
1456 (void)piece;
1457 if(dt_gui_widgets_suppressed()) return;
1458
1461
1462 // capture gui color picked event.
1463 if(self->picked_color_max[0] < self->picked_color_min[0]) return;
1464 const float *grayrgb = self->picked_color;
1465
1466 // normalize green:
1467 p->green = grayrgb[1] > 0.001f ? 1.0f / grayrgb[1] : 1.0f;
1468 p->red = fmaxf(0.0f, fminf(8.0f, (grayrgb[0] > 0.001f ? 1.0f / grayrgb[0] : 1.0f) / p->green));
1469 p->blue = fmaxf(0.0f, fminf(8.0f, (grayrgb[2] > 0.001f ? 1.0f / grayrgb[2] : 1.0f) / p->green));
1470 p->g2 = fmaxf(0.0f, fminf(8.0f, (grayrgb[3] > 0.001f ? 1.0f / grayrgb[3] : 1.0f) / p->green));
1471 p->green = 1.0;
1472
1475
1476 float tempK, tint;
1477 mul2temp(self, p, &tempK, &tint);
1478
1479 dt_bauhaus_slider_set(g->scale_k, tempK);
1480 dt_bauhaus_slider_set(g->scale_tint, tint);
1481 dt_bauhaus_slider_set(g->scale_r, p->red);
1482 dt_bauhaus_slider_set(g->scale_g, p->green);
1483 dt_bauhaus_slider_set(g->scale_b, p->blue);
1484 dt_bauhaus_slider_set(g->scale_g2, p->g2);
1485
1486 dt_bauhaus_combobox_set(g->presets, -1);
1488
1489 dt_dev_add_history_item(self->dev, self, TRUE, TRUE);
1490}
1491
1492
1493static void gui_sliders_update(struct dt_iop_module_t *self)
1494{
1495 const dt_image_t *img = &self->dev->image_storage;
1497
1498 if(FILTERS_ARE_CYGM(img->dsc.filters))
1499 {
1500 dt_bauhaus_widget_set_label(g->scale_r, N_("green"));
1501 gtk_widget_set_tooltip_text(g->scale_r, _("green channel coefficient"));
1502 dt_bauhaus_widget_set_label(g->scale_g, N_("magenta"));
1503 gtk_widget_set_tooltip_text(g->scale_g, _("magenta channel coefficient"));
1504 dt_bauhaus_widget_set_label(g->scale_b, N_("cyan"));
1505 gtk_widget_set_tooltip_text(g->scale_b, _("cyan channel coefficient"));
1506 dt_bauhaus_widget_set_label(g->scale_g2, N_("yellow"));
1507 gtk_widget_set_tooltip_text(g->scale_g2, _("yellow channel coefficient"));
1508
1509 gtk_box_reorder_child(GTK_BOX(g->cs.container), g->scale_b, 0);
1510 gtk_box_reorder_child(GTK_BOX(g->cs.container), g->scale_g2, 1);
1511 gtk_box_reorder_child(GTK_BOX(g->cs.container), g->scale_g, 2);
1512 gtk_box_reorder_child(GTK_BOX(g->cs.container), g->scale_r, 3);
1513 }
1514 else
1515 {
1516 dt_bauhaus_widget_set_label(g->scale_r, N_("red"));
1517 gtk_widget_set_tooltip_text(g->scale_r, _("red channel coefficient"));
1518 dt_bauhaus_widget_set_label(g->scale_g, N_("green"));
1519 gtk_widget_set_tooltip_text(g->scale_g, _("green channel coefficient"));
1520 dt_bauhaus_widget_set_label(g->scale_b, N_("blue"));
1521 gtk_widget_set_tooltip_text(g->scale_b, _("blue channel coefficient"));
1522 dt_bauhaus_widget_set_label(g->scale_g2, N_("emerald"));
1523 gtk_widget_set_tooltip_text(g->scale_g2, _("emerald channel coefficient"));
1524
1525 gtk_box_reorder_child(GTK_BOX(g->cs.container), g->scale_r, 0);
1526 gtk_box_reorder_child(GTK_BOX(g->cs.container), g->scale_g, 1);
1527 gtk_box_reorder_child(GTK_BOX(g->cs.container), g->scale_b, 2);
1528 gtk_box_reorder_child(GTK_BOX(g->cs.container), g->scale_g2, 3);
1529 }
1530
1531 gtk_widget_set_visible(GTK_WIDGET(g->scale_g2), (img->flags & DT_IMAGE_4BAYER));
1532}
1533
1534void gui_init(struct dt_iop_module_t *self)
1535{
1537
1538 g->colored_sliders = TRUE; // true if config != "no color"
1539 g->blackbody_is_confusing = FALSE; // true if config != "illuminant color"
1540
1541 const int feedback = TRUE;
1542 g->button_bar_visible = TRUE;
1543
1544 GtkBox *box_enabled = GTK_BOX(gtk_box_new(GTK_ORIENTATION_VERTICAL, DT_GUI_BOX_SPACING));
1545
1546 g->btn_asshot = dt_iop_togglebutton_new(self, N_("settings"), N_("as shot"), NULL,
1547 G_CALLBACK(btn_toggled), FALSE, 0, 0,
1549 gtk_widget_set_tooltip_text(g->btn_asshot, _("set white balance to as shot"));
1550
1551 // create color picker to be able to send its signal when spot selected,
1552 // this module may expect data in RAW or RGB, setting the color picker CST to IOP_CS_NONE will make the color
1553 // picker to depend on the number of color channels of the pixels. It is done like this as we may not know the
1554 // actual kind of data we are using in the GUI (it is part of the pipeline).
1557
1558 gtk_widget_set_tooltip_text(g->colorpicker, _("set white balance to detected from area"));
1559
1560 g->btn_user = dt_iop_togglebutton_new(self, N_("settings"), N_("user modified"), NULL,
1561 G_CALLBACK(btn_toggled), FALSE, 0, 0,
1563 gtk_widget_set_tooltip_text(g->btn_user, _("set white balance to user modified"));
1564
1565
1566 g->btn_d65 = dt_iop_togglebutton_new(self, N_("settings"), N_("camera reference"), NULL,
1567 G_CALLBACK(btn_toggled), FALSE, 0, 0,
1569 gtk_widget_set_tooltip_text(g->btn_d65, _("set white balance to camera reference point\nin most cases it should be D65"));
1570
1571 g->buttonbar = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, DT_GUI_BOX_SPACING); // put buttons at top. fill later.
1572 dt_gui_add_class(g->buttonbar, "dt_iop_toggle");
1573 gtk_box_pack_end(GTK_BOX(g->buttonbar), g->btn_d65, TRUE, TRUE, 0);
1574 gtk_box_pack_end(GTK_BOX(g->buttonbar), g->btn_user, TRUE, TRUE, 0);
1575 gtk_box_pack_end(GTK_BOX(g->buttonbar), g->colorpicker, TRUE, TRUE, 0);
1576 gtk_box_pack_end(GTK_BOX(g->buttonbar), g->btn_asshot, TRUE, TRUE, 0);
1577 gtk_box_pack_start(box_enabled, g->buttonbar, TRUE, TRUE, 0);
1578
1580 dt_bauhaus_widget_set_label(g->presets, N_("settings")); // relabel to settings to remove confusion between module presets and white balance settings
1581 gtk_widget_set_tooltip_text(g->presets, _("choose white balance setting"));
1582 gtk_box_pack_start(box_enabled, g->presets, TRUE, TRUE, 0);
1583
1584 g->mod_temp = NAN;
1585 for(int k = 0; k < 4; k++)
1586 {
1587 g->daylight_wb[k] = 1.0;
1588 g->as_shot_wb[k] = 1.f;
1589 }
1590
1591 GtkWidget *temp_label_box = gtk_event_box_new();
1592 g->temp_label = dt_ui_section_label_new(_("scene illuminant temp"));
1593 gtk_widget_set_tooltip_text(g->temp_label, _("click to cycle color mode on sliders"));
1594 gtk_container_add(GTK_CONTAINER(temp_label_box), g->temp_label);
1595
1596 gtk_box_pack_start(box_enabled, temp_label_box, TRUE, TRUE, 0);
1597
1598 //Match UI order: temp first, then tint (like every other app ever)
1600 0, 5000.0, 0, feedback);
1601 dt_bauhaus_slider_set_format(g->scale_k, " K");
1602 dt_bauhaus_widget_set_label(g->scale_k, N_("temperature"));
1603 gtk_widget_set_tooltip_text(g->scale_k, _("color temperature (in Kelvin)"));
1604 gtk_box_pack_start(box_enabled, g->scale_k, TRUE, TRUE, 0);
1605
1607 0, 1.0, 3, feedback);
1608 dt_bauhaus_widget_set_label(g->scale_tint, N_("tint"));
1609 gtk_widget_set_tooltip_text(g->scale_tint, _("color tint of the image, from magenta (value < 1) to green (value > 1)"));
1610 gtk_box_pack_start(box_enabled, g->scale_tint, TRUE, TRUE, 0);
1611
1613 (&g->cs,
1614 "plugins/darkroom/temperature/expand_coefficients",
1615 _("channel coefficients"),
1616 GTK_BOX(box_enabled), GTK_PACK_END);
1617
1618 self->widget = GTK_WIDGET(g->cs.container);
1619
1620 g->scale_r = dt_bauhaus_slider_from_params(self, N_("red"));
1621 g->scale_g = dt_bauhaus_slider_from_params(self, N_("green"));
1622 g->scale_b = dt_bauhaus_slider_from_params(self, N_("blue"));
1623 g->scale_g2 = dt_bauhaus_slider_from_params(self, "g2");
1624 dt_bauhaus_slider_set_digits(g->scale_r, 3);
1625 dt_bauhaus_slider_set_digits(g->scale_g, 3);
1626 dt_bauhaus_slider_set_digits(g->scale_b, 3);
1627 dt_bauhaus_slider_set_digits(g->scale_g2, 3);
1628
1629 gtk_widget_set_no_show_all(g->scale_g2, TRUE);
1630
1631 g_signal_connect(G_OBJECT(g->scale_k), "value-changed", G_CALLBACK(temp_tint_callback), self);
1632 g_signal_connect(G_OBJECT(g->scale_tint), "value-changed", G_CALLBACK(temp_tint_callback), self);
1633
1634 g_signal_connect(G_OBJECT(g->presets), "value-changed", G_CALLBACK(preset_tune_callback), self);
1635
1636 // start building top level widget
1637 self->widget = gtk_stack_new();
1638 gtk_stack_set_homogeneous(GTK_STACK(self->widget), FALSE);
1639
1640 GtkWidget *label_disabled = gtk_label_new(_("white balance disabled for camera"));
1641 gtk_widget_set_halign(label_disabled, GTK_ALIGN_START);
1642 gtk_label_set_ellipsize(GTK_LABEL(label_disabled), PANGO_ELLIPSIZE_END);
1643
1644 gtk_stack_add_named(GTK_STACK(self->widget), GTK_WIDGET(box_enabled), "enabled");
1645 gtk_stack_add_named(GTK_STACK(self->widget), label_disabled, "disabled");
1646}
1647
1649{
1651
1653}
1654
1655void gui_reset(struct dt_iop_module_t *self)
1656{
1658
1659 const int preset = dt_bauhaus_combobox_get(g->presets);
1661
1662 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(g->btn_asshot), preset == DT_IOP_TEMP_AS_SHOT);
1663 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(g->btn_user), preset == DT_IOP_TEMP_USER);
1664 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(g->btn_d65), preset == DT_IOP_TEMP_D65);
1665
1666 color_rgb_sliders(self);
1668}
1669
1670// clang-format off
1671// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
1672// vim: shiftwidth=2 expandtab tabstop=2 cindent
1673// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
1674// clang-format on
static void error(char *msg)
Definition ashift_lsd.c:202
#define TRUE
Definition ashift_lsd.c:162
#define FALSE
Definition ashift_lsd.c:158
void dt_bauhaus_slider_set_digits(GtkWidget *widget, int val)
Definition bauhaus.c:3549
void dt_bauhaus_combobox_clear(GtkWidget *widget)
Definition bauhaus.c:2192
void dt_bauhaus_slider_clear_stops(GtkWidget *widget)
Definition bauhaus.c:2367
void dt_bauhaus_slider_set_default(GtkWidget *widget, float def)
Definition bauhaus.c:1643
void dt_bauhaus_slider_set_stop(GtkWidget *widget, float stop, float r, float g, float b)
Definition bauhaus.c:2375
float dt_bauhaus_slider_get(GtkWidget *widget)
Definition bauhaus.c:3486
GtkWidget * dt_bauhaus_slider_new_with_range_and_feedback(dt_bauhaus_t *bh, dt_gui_module_t *self, float min, float max, float step, float defval, int digits, int feedback)
Definition bauhaus.c:1789
void dt_bauhaus_slider_set_feedback(GtkWidget *widget, int feedback)
Definition bauhaus.c:3595
int dt_bauhaus_combobox_get(GtkWidget *widget)
Definition bauhaus.c:2350
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_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
float dt_bauhaus_slider_get_hard_max(GtkWidget *widget)
Definition bauhaus.c:1602
void dt_bauhaus_combobox_add(GtkWidget *widget, const char *text)
Definition bauhaus.c:2019
#define DT_BAUHAUS_SLIDER_MAX_STOPS
Definition bauhaus.h:71
int width
Definition bilateral.h:1
int height
Definition bilateral.h:1
static const dt_aligned_pixel_simd_t const dt_adaptation_t const float p
@ IOP_CS_RAW
@ IOP_CS_RGB
@ IOP_CS_NONE
void dt_iop_color_picker_reset(dt_iop_module_t *module, gboolean keep)
GtkWidget * dt_color_picker_new_with_cst(dt_iop_module_t *module, dt_iop_color_picker_kind_t kind, GtkWidget *w, const dt_iop_colorspace_type_t cst)
@ DT_COLOR_PICKER_AREA
__DT_CLONE_TARGETS__ int dt_colorspaces_conversion_matrices_rgb(const float adobe_XYZ_to_CAM[4][3], double out_RGB_to_CAM[4][3], double out_CAM_to_RGB[3][4], const float *embedded_matrix, double mul[4])
int dt_colorspaces_conversion_matrices_xyz(const float adobe_XYZ_to_CAM[4][3], float in_XYZ_to_CAM[9], double XYZ_to_CAM[4][3], double CAM_to_XYZ[3][4])
static dt_aligned_pixel_t XYZ
const dt_colormatrix_t dt_aligned_pixel_t out
static const dt_colormatrix_t M
typedef void((*dt_cache_allocate_t)(void *userdata, dt_cache_entry_t *entry))
#define P(V, params)
#define S(V, params)
gboolean dt_image_is_matrix_correction_supported(const dt_image_t *img)
dt_image_pipe_class_t dt_image_pipe_class(const dt_image_t *img)
const char * dt_image_pipe_class_name(const dt_image_pipe_class_t klass)
gboolean dt_image_is_monochrome(const dt_image_t *img)
gboolean dt_image_needs_rawprepare(const dt_image_t *img)
gboolean dt_conf_is_equal(const char *name, const char *value)
void dt_control_log(const char *msg,...)
Definition control.c:777
darktable_t darktable
Definition darktable.c:183
void dt_print(dt_debug_thread_t thread, const char *msg,...)
Definition darktable.c:1600
#define dt_free_align(ptr)
Definition darktable.h:503
static void * dt_calloc_align(size_t size)
Definition darktable.h:510
@ DT_DEBUG_OPENCL
Definition darktable.h:744
#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
#define DT_MODULE_INTROSPECTION(MODVER, PARAMSTYPE)
Definition darktable.h:151
#define __DT_CLONE_TARGETS__
Definition darktable.h:379
#define __OMP_PARALLEL_FOR__(...)
Definition darktable.h:270
#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
static int FCxtrans(const int row, const int col, global const unsigned char(*const xtrans)[6])
static int FC(const int row, const int col, const unsigned int filters)
#define dt_dev_add_history_item(dev, module, enable, redraw)
void dt_iop_params_t
Definition dev_history.h:42
@ DT_DEV_PIXELPIPE_DISPLAY_MASK
Definition develop.h:118
void dtgtk_cairo_paint_bulb(cairo_t *cr, gint x, gint y, gint w, gint h, gint flags, void *data)
void dtgtk_cairo_paint_camera(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_drawn(cairo_t *cr, gint x, gint y, gint w, gint h, gint flags, void *data)
void default_output_format(dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece, dt_iop_buffer_dsc_t *dsc)
Definition format.c:75
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
void dt_gui_update_collapsible_section(dt_gui_collapsible_section_t *cs)
Definition gtk.c:3312
void dt_gui_add_class(GtkWidget *widget, const gchar *class_name)
Definition gtk.c:133
static GtkWidget * dt_ui_section_label_new(const gchar *str)
Definition gtk.h:469
#define DT_GUI_BOX_SPACING
Definition gtk.h:109
#define DT_GUI_MODULE(x)
@ DT_IMAGE_4BAYER
Definition image.h:127
#define FILTERS_ARE_CYGM(filters)
Definition imageio.h:48
const char ** dt_iop_set_description(dt_iop_module_t *module, const char *main_text, const char *purpose, const char *input, const char *process, const char *output)
Definition imageop.c:3220
@ DT_REQUEST_COLORPICK_OFF
Definition imageop.h:226
#define IOP_GUI_FREE
Definition imageop.h:641
#define dt_iop_fmt_log(module, fmt,...)
Debug helper to trace a module's input-format-driven decisions on the -d pipe channel (DT_DEBUG_PIPE)...
Definition imageop.h:490
@ IOP_FLAGS_ALLOW_TILING
Definition imageop.h:199
@ IOP_FLAGS_ONE_INSTANCE
Definition imageop.h:202
@ IOP_GROUP_TECHNICAL
Definition imageop.h:173
#define IOP_GUI_ALLOC(module)
Definition imageop.h:638
GtkWidget * dt_iop_togglebutton_new(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)
GtkWidget * dt_bauhaus_slider_from_params(dt_iop_module_t *self, const char *param)
Definition imageop_gui.c:77
void *const ovoid
static float kernel(const float *x, const float *y)
struct dt_iop_tonecurve_params_t preset
float *const restrict const size_t k
float *const restrict const size_t const size_t ch
float dt_aligned_pixel_t[4]
int dt_opencl_enqueue_kernel_2d(const int dev, const int kernel, const size_t *sizes)
Definition opencl.c:2164
int dt_opencl_create_kernel(const int prog, const char *name)
Definition opencl.c:2058
void * dt_opencl_copy_host_to_device_constant(const int devid, const size_t size, void *host)
Definition opencl.c:2360
void dt_opencl_free_kernel(const int kernel)
Definition opencl.c:2101
int dt_opencl_set_kernel_arg(const int dev, const int kernel, const int num, const size_t size, const void *arg)
Definition opencl.c:2155
void dt_opencl_release_mem_object(cl_mem mem)
Definition opencl.c:2415
#define ROUNDUPDHT(a, b)
Definition opencl.h:82
#define ROUNDUPDWD(a, b)
Definition opencl.h:81
struct _GtkWidget GtkWidget
Definition splash.h:29
const float uint32_t state[4]
struct dt_bauhaus_t * bauhaus
Definition darktable.h:806
struct dt_develop_t * develop
Definition darktable.h:798
dt_iop_buffer_dsc_t dsc_out
dt_iop_buffer_dsc_t dsc_in
struct dt_iop_module_t *void * data
dt_image_t image_storage
Definition develop.h:259
gboolean wb_is_D65
Definition develop.h:447
dt_aligned_pixel_t wb_coeffs
Definition develop.h:448
struct dt_develop_t::@20 proxy
gboolean camera_missing_sample
Definition image.h:302
char camera_makermodel[128]
Definition image.h:300
int32_t flags
Definition image.h:319
float d65_color_matrix[9]
Definition image.h:339
dt_iop_buffer_dsc_t dsc
Definition image.h:337
float adobe_XYZ_to_CAM[4][3]
Definition image.h:362
char filename[DT_MAX_FILENAME_LEN]
Definition image.h:304
int32_t id
Definition image.h:319
dt_aligned_pixel_t wb_coeffs
Definition image.h:359
struct dt_iop_buffer_dsc_t::@33 temperature
dt_aligned_pixel_t coeffs
Definition format.h:81
uint32_t filters
Definition format.h:60
unsigned int channels
Definition format.h:54
uint8_t xtrans[6][6]
Definition format.h:70
dt_aligned_pixel_t processed_maximum
Definition format.h:85
dt_iop_global_data_t * data
Definition imageop.h:263
dt_dev_request_colorpick_flags_t request_color_pick
Definition imageop.h:294
GtkDarktableToggleButton * off
Definition imageop.h:376
int32_t hide_enable_button
Definition imageop.h:292
dt_iop_params_t * default_params
Definition imageop.h:344
GtkWidget * widget
Definition imageop.h:374
struct dt_develop_t * dev
Definition imageop.h:333
dt_iop_gui_data_t * gui_data
Definition imageop.h:348
gboolean default_enabled
Definition imageop.h:340
dt_iop_global_data_t * global_data
Definition imageop.h:351
dt_aligned_pixel_t picked_color_min
Definition imageop.h:309
dt_aligned_pixel_t picked_color_max
Definition imageop.h:309
dt_aligned_pixel_t picked_color
Definition imageop.h:309
dt_iop_params_t * params
Definition imageop.h:344
Region of interest passed through the pixelpipe.
Definition imageop.h:72
dt_gui_collapsible_section_t cs
#define DT_IOP_HIGHEST_TINT
Definition temperature.c:96
static cmsCIEXYZ temperature_tint_to_XYZ(double TempK, double tint)
void commit_params(struct dt_iop_module_t *self, dt_iop_params_t *p1, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
static double spd_blackbody(unsigned long int wavelength, double TempK)
const char ** description(struct dt_iop_module_t *self)
int default_group()
static cmsCIEXYZ spectrum_to_XYZ(double TempK, spd I)
__DT_CLONE_TARGETS__ int process(struct dt_iop_module_t *self, const dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece, const void *const ivoid, void *const ovoid)
static cmsCIEXYZ mul2xyz(dt_iop_module_t *self, const dt_iop_temperature_params_t *p)
static void _publish_wb_coeffs(dt_iop_module_t *self, const dt_iop_temperature_params_t *p)
void reload_defaults(dt_iop_module_t *module)
static double spd_daylight(unsigned long int wavelength, double TempK)
#define INITIALBLACKBODYTEMPERATURE
Definition temperature.c:90
static void temp2mul(dt_iop_module_t *self, double TempK, double tint, double mul[4])
#define DT_IOP_LOWEST_TINT
Definition temperature.c:95
void color_rgb_sliders(struct dt_iop_module_t *self)
static void gui_sliders_update(struct dt_iop_module_t *self)
static void XYZ_to_temperature(cmsCIEXYZ XYZ, float *TempK, float *tint)
#define DT_IOP_LOWEST_TEMPERATURE
Definition temperature.c:92
static int ignore_missing_wb(dt_image_t *img)
static int calculate_bogus_daylight_wb(dt_iop_module_t *module, double bwb[4])
static cmsCIEXYZ temperature_to_XYZ(double TempK)
static void _temp_array_from_params(double a[4], const dt_iop_temperature_params_t *p)
void init_pipe(struct dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
const char * name()
void output_format(dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece, dt_iop_buffer_dsc_t *dsc)
void gui_reset(struct dt_iop_module_t *self)
#define DT_IOP_HIGHEST_TEMPERATURE
Definition temperature.c:93
static gboolean btn_toggled(GtkWidget *togglebutton, GdkEventButton *event, dt_iop_module_t *self)
void gui_update(struct dt_iop_module_t *self)
Refresh GUI controls from current params and configuration.
void gui_init(struct dt_iop_module_t *self)
void gui_changed(dt_iop_module_t *self, GtkWidget *w, void *previous)
#define DT_IOP_TEMP_D65
#define DT_IOP_TEMP_AS_SHOT
void cleanup_global(dt_iop_module_so_t *module)
void commit_proxy(dt_iop_module_t *self)
int default_colorspace(dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece)
int flags()
static void _temp_params_from_array(dt_iop_temperature_params_t *p, const double a[4])
static void mul2temp(dt_iop_module_t *self, dt_iop_temperature_params_t *p, float *TempK, float *tint)
static void temp_tint_callback(GtkWidget *slider, dt_iop_module_t *self)
void gui_cleanup(struct dt_iop_module_t *self)
typedef double((*spd)(unsigned long int wavelength, double TempK))
gboolean force_enable(struct dt_iop_module_t *self, const gboolean current_state)
static void find_coeffs(dt_iop_module_t *module, double coeffs[4])
#define DT_IOP_NUM_OF_STD_TEMP_PRESETS
Definition temperature.c:98
void cleanup_pipe(struct dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
static void prepare_matrices(dt_iop_module_t *module)
void init_global(dt_iop_module_so_t *module)
#define DT_IOP_TEMP_SPOT
int process_cl(struct dt_iop_module_t *self, const dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece, cl_mem dev_in, cl_mem dev_out)
static void xyz2mul(dt_iop_module_t *self, cmsCIEXYZ xyz, double mul[4])
void color_picker_apply(dt_iop_module_t *self, GtkWidget *picker, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
void color_temptint_sliders(struct dt_iop_module_t *self)
static void preset_tune_callback(GtkWidget *widget, dt_iop_module_t *self)
#define DT_IOP_TEMP_USER
int legacy_params(dt_iop_module_t *self, const void *const old_params, const int old_version, void *new_params, const int new_version)
static int _max(int a, int b)
Definition tiling.c:88
void dtgtk_togglebutton_set_paint(GtkDarktableToggleButton *button, DTGTKCairoPaintIconFunc paint, gint paintflags, void *paintdata)
#define DTGTK_TOGGLEBUTTON(obj)