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);
107
109{
110 float red; // $MIN: 0.0 $MAX: 8.0
111 float green; // $MIN: 0.0 $MAX: 8.0
112 float blue; // $MIN: 0.0 $MAX: 8.0
113 float g2; // $MIN: 0.0 $MAX: 8.0 $DESCRIPTION: "emerald"
115
139
144
151
158
159int legacy_params(dt_iop_module_t *self, const void *const old_params, const int old_version,
160 void *new_params, const int new_version)
161{
162 if(old_version == 2 && new_version == 3)
163 {
164 typedef struct dt_iop_temperature_params_v2_t
165 {
166 float temp_out;
167 float coeffs[3];
168 } dt_iop_temperature_params_v2_t;
169
170 dt_iop_temperature_params_v2_t *o = (dt_iop_temperature_params_v2_t *)old_params;
172
173 n->red = o->coeffs[0];
174 n->green = o->coeffs[1];
175 n->blue = o->coeffs[2];
176 n->g2 = NAN;
177
178 return 0;
179 }
180 return 1;
181}
182
183static inline void _temp_params_from_array(dt_iop_temperature_params_t *p, const double a[4])
184{
185 p->red = a[0]; p->green = a[1]; p->blue = a[2]; p->g2 = a[3];
186}
187
188static inline void _temp_array_from_params(double a[4], const dt_iop_temperature_params_t *p)
189{
190 a[0] = p->red; a[1] = p->green; a[2] = p->blue; a[3] = p->g2;
191}
192
194{
195 // Ignore files that end with "-hdr.dng" since these are broken files we
196 // generated without any proper WB tagged
197 if(g_str_has_suffix(img->filename,"-hdr.dng"))
198 return TRUE;
199
200 static const char *const ignored_cameras[] = {
201 "Canon PowerShot A610",
202 "Canon PowerShot S3 IS",
203 "Canon PowerShot A620",
204 "Canon PowerShot A720 IS",
205 "Canon PowerShot A630",
206 "Canon PowerShot A640",
207 "Canon PowerShot A650",
208 "Canon PowerShot SX110 IS",
209 "Mamiya ZD",
210 "Canon EOS D2000C",
211 "Kodak EOS DCS 1",
212 "Kodak DCS560C",
213 "Kodak DCS460D",
214 "Nikon E5700",
215 "Sony DSC-F828",
216 "GITUP GIT2",
217 };
218
219 for(int i=0; i < sizeof(ignored_cameras)/sizeof(ignored_cameras[1]); i++)
220 if(!strcmp(img->camera_makermodel, ignored_cameras[i]))
221 return TRUE;
222
223 return FALSE;
224}
225
226
227const char *name()
228{
229 return C_("modulename", "white balance");
230}
231
232const char **description(struct dt_iop_module_t *self)
233{
234 return dt_iop_set_description(self, _("scale raw RGB channels to balance white and help demosaicing"),
235 _("corrective"),
236 _("linear, raw, scene-referred"),
237 _("linear, raw"),
238 _("linear, raw, scene-referred"));
239}
240
242{
243 return IOP_GROUP_TECHNICAL;
244}
245
250
252{
253 // This module may work in RAW or RGB (e.g. for TIFF files) depending on the input
254 // The module does not change the color space between the input and output, therefore implement it here
255 if(piece && piece->dsc_in.cst != IOP_CS_RAW)
256 return IOP_CS_RGB;
257 return IOP_CS_RAW;
258}
259
262{
263 default_output_format(self, pipe, piece, dsc);
264}
265
266/*
267 * Spectral power distribution functions
268 * https://en.wikipedia.org/wiki/Spectral_power_distribution
269 */
270typedef double((*spd)(unsigned long int wavelength, double TempK));
271
272/*
273 * Bruce Lindbloom, "Spectral Power Distribution of a Blackbody Radiator"
274 * http://www.brucelindbloom.com/Eqn_Blackbody.html
275 */
276static double spd_blackbody(unsigned long int wavelength, double TempK)
277{
278 // convert wavelength from nm to m
279 const long double lambda = (double)wavelength * 1e-9;
280
281/*
282 * these 2 constants were computed using following Sage code:
283 *
284 * (from http://physics.nist.gov/cgi-bin/cuu/Value?h)
285 * h = 6.62606957 * 10^-34 # Planck
286 * c= 299792458 # speed of light in vacuum
287 * k = 1.3806488 * 10^-23 # Boltzmann
288 *
289 * c_1 = 2 * pi * h * c^2
290 * c_2 = h * c / k
291 *
292 * print 'c_1 = ', c_1, ' ~= ', RealField(128)(c_1)
293 * print 'c_2 = ', c_2, ' ~= ', RealField(128)(c_2)
294 */
295
296#define c1 3.7417715246641281639549488324352159753e-16L
297#define c2 0.014387769599838156481252937624049081933L
298
299 return (double)(c1 / (powl(lambda, 5) * (expl(c2 / (lambda * TempK)) - 1.0L)));
300
301#undef c2
302#undef c1
303}
304
305/*
306 * Bruce Lindbloom, "Spectral Power Distribution of a CIE D-Illuminant"
307 * http://www.brucelindbloom.com/Eqn_DIlluminant.html
308 * and https://en.wikipedia.org/wiki/Standard_illuminant#Illuminant_series_D
309 */
310static double spd_daylight(unsigned long int wavelength, double TempK)
311{
312 cmsCIExyY WhitePoint = { 0.3127, 0.3290, 1.0 };
313
314 /*
315 * Bruce Lindbloom, "TempK to xy"
316 * http://www.brucelindbloom.com/Eqn_T_to_xy.html
317 */
318 cmsWhitePointFromTemp(&WhitePoint, TempK);
319
320 const double M = (0.0241 + 0.2562 * WhitePoint.x - 0.7341 * WhitePoint.y),
321 m1 = (-1.3515 - 1.7703 * WhitePoint.x + 5.9114 * WhitePoint.y) / M,
322 m2 = (0.0300 - 31.4424 * WhitePoint.x + 30.0717 * WhitePoint.y) / M;
323
324 const unsigned long int j
325 = ((wavelength - cie_daylight_components[0].wavelength)
326 / (cie_daylight_components[1].wavelength - cie_daylight_components[0].wavelength));
327
328 return (cie_daylight_components[j].S[0] + m1 * cie_daylight_components[j].S[1]
329 + m2 * cie_daylight_components[j].S[2]);
330}
331
332/*
333 * Bruce Lindbloom, "Computing XYZ From Spectral Data (Emissive Case)"
334 * http://www.brucelindbloom.com/Eqn_Spect_to_XYZ.html
335 */
336static cmsCIEXYZ spectrum_to_XYZ(double TempK, spd I)
337{
338 cmsCIEXYZ Source = {.X = 0.0, .Y = 0.0, .Z = 0.0 };
339
340 /*
341 * Color matching functions
342 * https://en.wikipedia.org/wiki/CIE_1931_color_space#Color_matching_functions
343 */
344 for(size_t i = 0; i < cie_1931_std_colorimetric_observer_count; i++)
345 {
346 const unsigned long int lambda = cie_1931_std_colorimetric_observer[0].wavelength
347 + (cie_1931_std_colorimetric_observer[1].wavelength
348 - cie_1931_std_colorimetric_observer[0].wavelength) * i;
349 const double P = I(lambda, TempK);
350 Source.X += P * cie_1931_std_colorimetric_observer[i].xyz.X;
351 Source.Y += P * cie_1931_std_colorimetric_observer[i].xyz.Y;
352 Source.Z += P * cie_1931_std_colorimetric_observer[i].xyz.Z;
353 }
354
355 // normalize so that each component is in [0.0, 1.0] range
356 const double _max = fmax(fmax(Source.X, Source.Y), Source.Z);
357 Source.X /= _max;
358 Source.Y /= _max;
359 Source.Z /= _max;
360
361 return Source;
362}
363
364// TODO: temperature and tint cannot be disjoined! (here it assumes no tint)
365static cmsCIEXYZ temperature_to_XYZ(double TempK)
366{
369
371 {
372 // if temperature is less than 4000K we use blackbody,
373 // because there will be no Daylight reference below 4000K...
374 return spectrum_to_XYZ(TempK, spd_blackbody);
375 }
376 else
377 {
378 return spectrum_to_XYZ(TempK, spd_daylight);
379 }
380}
381
382static cmsCIEXYZ temperature_tint_to_XYZ(double TempK, double tint)
383{
384 cmsCIEXYZ xyz = temperature_to_XYZ(TempK);
385
386 xyz.Y /= tint; // TODO: This is baaad!
387
388 return xyz;
389}
390
391// binary search inversion
392static void XYZ_to_temperature(cmsCIEXYZ XYZ, float *TempK, float *tint)
393{
394 double maxtemp = DT_IOP_HIGHEST_TEMPERATURE, mintemp = DT_IOP_LOWEST_TEMPERATURE;
395 cmsCIEXYZ _xyz;
396
397 for(*TempK = (maxtemp + mintemp) / 2.0; (maxtemp - mintemp) > 1.0; *TempK = (maxtemp + mintemp) / 2.0)
398 {
399 _xyz = temperature_to_XYZ(*TempK);
400 if(_xyz.Z / _xyz.X > XYZ.Z / XYZ.X)
401 maxtemp = *TempK;
402 else
403 mintemp = *TempK;
404 }
405
406 *tint = (_xyz.Y / _xyz.X) / (XYZ.Y / XYZ.X); // TODO: Fix this to move orthogonally to planckian locus
407
408
411 if(*tint < DT_IOP_LOWEST_TINT) *tint = DT_IOP_LOWEST_TINT;
412 if(*tint > DT_IOP_HIGHEST_TINT) *tint = DT_IOP_HIGHEST_TINT;
413}
414
415static void xyz2mul(dt_iop_module_t *self, cmsCIEXYZ xyz, double mul[4])
416{
418
419 double XYZ[3] = { xyz.X, xyz.Y, xyz.Z };
420
421 double CAM[4];
422 for(int k = 0; k < 4; k++)
423 {
424 CAM[k] = 0.0;
425 for(int i = 0; i < 3; i++)
426 {
427 CAM[k] += g->XYZ_to_CAM[k][i] * XYZ[i];
428 }
429 }
430
431 for(int k = 0; k < 4; k++) mul[k] = 1.0 / CAM[k];
432}
433
434static void temp2mul(dt_iop_module_t *self, double TempK, double tint, double mul[4])
435{
436 cmsCIEXYZ xyz = temperature_to_XYZ(TempK);
437
438 xyz.Y /= tint; // TODO: This is baaad!
447 xyz2mul(self, xyz, mul);
448}
449
451{
453
454 double CAM[4];
456 for(int k = 0; k < 4; k++) CAM[k] = CAM[k] > 0.0f ? 1.0 / CAM[k] : 0.0f;
457
458 double XYZ[3];
459 for(int k = 0; k < 3; k++)
460 {
461 XYZ[k] = 0.0;
462 for(int i = 0; i < 4; i++)
463 {
464 XYZ[k] += g->CAM_to_XYZ[k][i] * CAM[i];
465 }
466 }
467
468 return (cmsCIEXYZ){ XYZ[0], XYZ[1], XYZ[2] };
469}
470
471static void mul2temp(dt_iop_module_t *self, dt_iop_temperature_params_t *p, float *TempK, float *tint)
472{
473 XYZ_to_temperature(mul2xyz(self, p), TempK, tint);
474}
475
477int process(struct dt_iop_module_t *self, const dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece, const void *const ivoid,
478 void *const ovoid)
479{
480 const dt_iop_roi_t *const roi_out = &piece->roi_out;
481 const uint32_t filters = piece->dsc_in.filters;
482 const uint8_t(*const xtrans)[6] = (const uint8_t(*const)[6])piece->dsc_in.xtrans;
484 const int width = roi_out->width;
485 const int height = roi_out->height;
486
487 const float *const in = (const float *const)ivoid;
488 float *const out = (float *const)ovoid;
489 const float *const d_coeffs = d->coeffs;
490
491 if(filters == 9u)
492 { // xtrans float mosaiced
494 for(int j = 0; j < height; j++)
495 {
496 const size_t row_start = (size_t)j * width;
497 const float coeffs[12] =
498 {
499 d_coeffs[FCxtrans(j, 0, roi_out, xtrans)], d_coeffs[FCxtrans(j, 1, roi_out, xtrans)],
500 d_coeffs[FCxtrans(j, 2, roi_out, xtrans)], d_coeffs[FCxtrans(j, 3, roi_out, xtrans)],
501 d_coeffs[FCxtrans(j, 4, roi_out, xtrans)], d_coeffs[FCxtrans(j, 5, roi_out, xtrans)],
502 d_coeffs[FCxtrans(j, 6, roi_out, xtrans)], d_coeffs[FCxtrans(j, 7, roi_out, xtrans)],
503 d_coeffs[FCxtrans(j, 8, roi_out, xtrans)], d_coeffs[FCxtrans(j, 9, roi_out, xtrans)],
504 d_coeffs[FCxtrans(j, 10, roi_out, xtrans)], d_coeffs[FCxtrans(j, 11, roi_out, xtrans)],
505 };
506 /* Keep the 12-sensel X-Trans period explicit, but let the compiler pick
507 its own vectorization strategy on the scalar multiplies. */
508 int i = 0;
509 for(; i + 11 < width; i += 12)
510 {
511 const size_t p = row_start + i;
512 out[p + 0] = in[p + 0] * coeffs[0];
513 out[p + 1] = in[p + 1] * coeffs[1];
514 out[p + 2] = in[p + 2] * coeffs[2];
515 out[p + 3] = in[p + 3] * coeffs[3];
516 out[p + 4] = in[p + 4] * coeffs[4];
517 out[p + 5] = in[p + 5] * coeffs[5];
518 out[p + 6] = in[p + 6] * coeffs[6];
519 out[p + 7] = in[p + 7] * coeffs[7];
520 out[p + 8] = in[p + 8] * coeffs[8];
521 out[p + 9] = in[p + 9] * coeffs[9];
522 out[p + 10] = in[p + 10] * coeffs[10];
523 out[p + 11] = in[p + 11] * coeffs[11];
524 }
525 for(; i < width; i++)
526 {
527 const size_t p = row_start + i;
528 out[p] = in[p] * coeffs[i % 12];
529 }
530 }
531
532 }
533 else if(filters)
534 { // bayer float mosaiced
535 const int cfa_x = roi_out->x & 1;
537 for(int j = 0; j < height; j++)
538 {
539 const int offset_j = j + roi_out->y;
540 const size_t row_start = (size_t)j * width;
541 const int id0 = FC(offset_j, cfa_x + 0, filters);
542 const int id1 = FC(offset_j, cfa_x + 1, filters);
543 const float coeff0 = d_coeffs[id0];
544 const float coeff1 = d_coeffs[id1];
545 int i = 0;
546
547 for(; i + 1 < width; i += 2)
548 {
549 const size_t p = row_start + i;
550 out[p + 0] = in[p + 0] * coeff0;
551 out[p + 1] = in[p + 1] * coeff1;
552 }
553 for(; i < width; i++)
554 {
555 const size_t p = row_start + i;
556 out[p] = in[p] * d_coeffs[FC(offset_j, i + roi_out->x, filters)];
557 }
558 }
559
560 }
561 else
562 { // non-mosaiced
563 const size_t ch = piece->dsc_in.channels;
564 const size_t npixels = (size_t)roi_out->width * roi_out->height;
565
566 if(ch == 4)
567 {
569 for(size_t k = 0; k < npixels; k++)
570 {
571 const size_t p = 4 * k;
572 out[p + 0] = in[p + 0] * d->coeffs[0];
573 out[p + 1] = in[p + 1] * d->coeffs[1];
574 out[p + 2] = in[p + 2] * d->coeffs[2];
575 out[p + 3] = in[p + 3];
576 }
577
578 }
579 else
580 {
582 for(size_t k = 0; k < ch * npixels; k += ch)
583 {
584 for(ptrdiff_t c = 0; c < 3; c++)
585 {
586 const size_t p = k + c;
587 out[p] = in[p] * d->coeffs[c];
588 }
589 }
590
591 }
592
594 dt_iop_alpha_copy(ivoid, ovoid, roi_out->width, roi_out->height);
595 }
596
597 return 0;
598}
599
600#ifdef HAVE_OPENCL
601int 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)
602{
603 const dt_iop_roi_t *const roi_in = &piece->roi_in;
604 const dt_iop_roi_t *const roi_out = &piece->roi_out;
607
608 const int devid = pipe->devid;
609 const uint32_t filters = piece->dsc_in.filters;
610 cl_mem dev_coeffs = NULL;
611 cl_mem dev_xtrans = NULL;
612 cl_int err = -999;
613 int kernel = -1;
614
615 if(filters == 9u)
616 {
618 }
619 else if(filters)
620 {
622 }
623 else
624 {
626 }
627
628 if(filters == 9u)
629 {
630 dev_xtrans = dt_opencl_copy_host_to_device_constant(devid, sizeof(piece->dsc_in.xtrans), (void *)piece->dsc_in.xtrans);
631 if(IS_NULL_PTR(dev_xtrans)) goto error;
632 }
633
634 dev_coeffs = dt_opencl_copy_host_to_device_constant(devid, sizeof(float) * 3, d->coeffs);
635 if(IS_NULL_PTR(dev_coeffs)) goto error;
636
637 const int width = roi_in->width;
638 const int height = roi_in->height;
639
640 size_t sizes[] = { ROUNDUPDWD(width, devid), ROUNDUPDHT(height, devid), 1 };
641 dt_opencl_set_kernel_arg(devid, kernel, 0, sizeof(cl_mem), (void *)&dev_in);
642 dt_opencl_set_kernel_arg(devid, kernel, 1, sizeof(cl_mem), (void *)&dev_out);
643 dt_opencl_set_kernel_arg(devid, kernel, 2, sizeof(int), (void *)&width);
644 dt_opencl_set_kernel_arg(devid, kernel, 3, sizeof(int), (void *)&height);
645 dt_opencl_set_kernel_arg(devid, kernel, 4, sizeof(cl_mem), (void *)&dev_coeffs);
646 dt_opencl_set_kernel_arg(devid, kernel, 5, sizeof(uint32_t), (void *)&filters);
647 dt_opencl_set_kernel_arg(devid, kernel, 6, sizeof(uint32_t), (void *)&roi_out->x);
648 dt_opencl_set_kernel_arg(devid, kernel, 7, sizeof(uint32_t), (void *)&roi_out->y);
649 dt_opencl_set_kernel_arg(devid, kernel, 8, sizeof(cl_mem), (void *)&dev_xtrans);
650 err = dt_opencl_enqueue_kernel_2d(devid, kernel, sizes);
651 if(err != CL_SUCCESS) goto error;
652
655
656 return TRUE;
657
658error:
661 dt_print(DT_DEBUG_OPENCL, "[opencl_white_balance] couldn't enqueue kernel! %d\n", err);
662 return FALSE;
663}
664#endif
665
666gboolean force_enable(struct dt_iop_module_t *self, const gboolean current_state)
667{
668 // History sanitization: white balance has nothing to balance on a true monochrome sensor, so a
669 // WB entry pasted onto a monochrome image is forced off here, at history-read time. For every
670 // other image (including non-raw, where manual WB is allowed) the user's state is preserved.
671 // This mirrors reload_defaults(), which hides the on/off button for monochrome images.
672 const gboolean mono = dt_image_is_monochrome(&self->dev->image_storage);
673 const gboolean state = current_state && !mono;
674 dt_iop_fmt_log(self, "force_enable: class=%s mono=%d current=%d -> %d",
676 mono, current_state, state);
677 return state;
678}
679
682{
686
687 d->coeffs[0] = p->red;
688 d->coeffs[1] = p->green;
689 d->coeffs[2] = p->blue;
690 // Legacy-history / non-4-colour safety net: older edits may have stored g2 = NaN (the historical
691 // "usually NAN for RGB" second-green value, see find_coeffs). A NaN/0 multiplier would poison
692 // half the green CFA sites, so fall back to the first green when g2 isn't a usable number.
693 const gboolean g2_usable = isnormal(p->g2) || (self->dev->image_storage.flags & DT_IMAGE_4BAYER);
694 d->coeffs[3] = g2_usable ? p->g2 : p->green;
695 dt_iop_fmt_log(self, "commit: class=%s matrix_supported=%d coeffs=[%.4f %.4f %.4f %.4f] g2_in=%.4f g2_usable=%d -> enabled=%d",
698 d->coeffs[0], d->coeffs[1], d->coeffs[2], d->coeffs[3], p->g2, g2_usable, piece->enabled);
699 for(int k = 0; k < 4; k++) self->dev->proxy.wb_coeffs[k] = d->coeffs[k];
700 piece->dsc_out.temperature.enabled = 1;
701 for(int k = 0; k < 4; k++)
702 {
703 piece->dsc_out.temperature.coeffs[k] = d->coeffs[k];
704 piece->dsc_out.processed_maximum[k] = piece->dsc_in.processed_maximum[k] * d->coeffs[k];
705 }
706
707 // 4Bayer images not implemented in OpenCL yet
709
710 if(!IS_NULL_PTR(g))
711 {
712 // advertise on the pipe if coeffs are D65 for validity check
713 gboolean is_D65 = TRUE;
714 for(int c = 0; c < 3; c++)
715 if(d->coeffs[c] != (float)g->daylight_wb[c]) is_D65 = FALSE;
716
717 self->dev->proxy.wb_is_D65 = is_D65;
718 }
719}
720
722{
724 piece->data_size = sizeof(dt_iop_temperature_data_t);
725}
726
728{
729 dt_free_align(piece->data);
730 piece->data = NULL;
731}
732
734{
736
737 const gboolean color_rgb = g->colored_sliders &&
739
744 dt_bauhaus_slider_set_feedback(g->scale_r, !color_rgb);
745 dt_bauhaus_slider_set_feedback(g->scale_g, !color_rgb);
746 dt_bauhaus_slider_set_feedback(g->scale_b, !color_rgb);
747 dt_bauhaus_slider_set_feedback(g->scale_g2, !color_rgb);
748
749 if(!color_rgb) return;
750
751 // there are 3 ways to do colored sliders: naive (independent 0->1), smart(er) (dependent 0->1) and real (coeff)
752
753 if(FALSE)
754 {
755 //naive:
756 dt_bauhaus_slider_set_stop(g->scale_r, 0.0, 0.0, 0.0, 0.0);
757 dt_bauhaus_slider_set_stop(g->scale_r, 1.0, 1.0, 0.0, 0.0);
758
759 dt_bauhaus_slider_set_stop(g->scale_g, 0.0, 0.0, 0.0, 0.0);
760 dt_bauhaus_slider_set_stop(g->scale_g, 1.0, 0.0, 1.0, 0.0);
761
762 dt_bauhaus_slider_set_stop(g->scale_b, 0.0, 0.0, 0.0, 0.0);
763 dt_bauhaus_slider_set_stop(g->scale_b, 1.0, 0.0, 0.0, 1.0);
764
765 dt_bauhaus_slider_set_stop(g->scale_g2, 0.0, 0.0, 0.0, 0.0);
766 dt_bauhaus_slider_set_stop(g->scale_g2, 1.0, 0.0, 1.0, 0.0);
767 }
768 if(!g->blackbody_is_confusing)
769 {
770 //smart(er) than naive
771 const float rchan = dt_bauhaus_slider_get(g->scale_r) / dt_bauhaus_slider_get_hard_max(g->scale_r);
772 const float gchan = dt_bauhaus_slider_get(g->scale_g) / dt_bauhaus_slider_get_hard_max(g->scale_g);
773 const float bchan = dt_bauhaus_slider_get(g->scale_b) / dt_bauhaus_slider_get_hard_max(g->scale_b);
774
775 dt_bauhaus_slider_set_stop(g->scale_r, 0.0, 0.0, gchan, bchan);
776 dt_bauhaus_slider_set_stop(g->scale_r, 1.0, 1.0, gchan, bchan);
777
778 dt_bauhaus_slider_set_stop(g->scale_g, 0.0, rchan, 0.0, bchan);
779 dt_bauhaus_slider_set_stop(g->scale_g, 1.0, rchan, 1.0, bchan);
780
781 dt_bauhaus_slider_set_stop(g->scale_b, 0.0, rchan, gchan, 0.0);
782 dt_bauhaus_slider_set_stop(g->scale_b, 1.0, rchan, gchan, 1.0);
783 }
784 else
785 {
786 //real (ish)
787 //we consider daylight wb to be "reference white"
788 const double white[3] = {
789 1.0/g->daylight_wb[0],
790 1.0/g->daylight_wb[1],
791 1.0/g->daylight_wb[2],
792 };
793
794 const float rchanmul = dt_bauhaus_slider_get(g->scale_r);
795 const float rchanmulmax = dt_bauhaus_slider_get_hard_max(g->scale_r);
796 const float gchanmul = dt_bauhaus_slider_get(g->scale_g);
797 const float gchanmulmax = dt_bauhaus_slider_get_hard_max(g->scale_g);
798 const float bchanmul = dt_bauhaus_slider_get(g->scale_b);
799 const float bchanmulmax = dt_bauhaus_slider_get_hard_max(g->scale_g);
800
801 dt_bauhaus_slider_set_stop(g->scale_r, 0.0, white[0]*0.0, white[1]*gchanmul, white[2]*bchanmul);
802 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);
803 dt_bauhaus_slider_set_stop(g->scale_r, 1.0, white[0]*1.0, white[1]*(gchanmul/gchanmulmax), white[2]*(bchanmul/bchanmulmax));
804
805 dt_bauhaus_slider_set_stop(g->scale_g, 0.0, white[0]*rchanmul, white[1]*0.0, white[2]*bchanmul);
806 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);
807 dt_bauhaus_slider_set_stop(g->scale_g, 1.0, white[0]*(rchanmul/rchanmulmax), white[1]*1.0, white[2]*(bchanmul/bchanmulmax));
808
809 dt_bauhaus_slider_set_stop(g->scale_b, 0.0, white[0]*rchanmul, white[1]*gchanmul, white[2]*0.0);
810 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]);
811 dt_bauhaus_slider_set_stop(g->scale_b, 1.0, white[0]*(rchanmul/rchanmulmax), white[1]*(gchanmul/gchanmulmax), white[2]*1.0);
812 }
813
814 if(gtk_widget_get_visible(GTK_WIDGET(g->scale_r)))
815 {
816 gtk_widget_queue_draw(GTK_WIDGET(g->scale_r));
817 gtk_widget_queue_draw(GTK_WIDGET(g->scale_g));
818 gtk_widget_queue_draw(GTK_WIDGET(g->scale_b));
819 }
820}
821
823{
825
827 dt_bauhaus_slider_clear_stops(g->scale_tint);
828 dt_bauhaus_slider_set_feedback(g->scale_k, !g->colored_sliders);
829 dt_bauhaus_slider_set_feedback(g->scale_tint, !g->colored_sliders);
830
831 if(!g->colored_sliders) return;
832
834 const double tint_step = (double)(DT_IOP_HIGHEST_TINT - DT_IOP_LOWEST_TINT) / (DT_BAUHAUS_SLIDER_MAX_STOPS - 1.0);
835 const int blackbody_is_confusing = g->blackbody_is_confusing;
836
837 const float cur_temp = dt_bauhaus_slider_get(g->scale_k);
838 const float cur_tint = dt_bauhaus_slider_get(g->scale_tint);
839
840 //we consider daylight wb to be "reference white"
841 const double dayligh_white[3] = {
842 1.0/g->daylight_wb[0],
843 1.0/g->daylight_wb[1],
844 1.0/g->daylight_wb[2],
845 };
846
847 double cur_coeffs[4] = {0.0};
848 temp2mul(self, cur_temp, 1.0, cur_coeffs);
849 const double cur_white[3] = {
850 1.0/cur_coeffs[0],
851 1.0/cur_coeffs[1],
852 1.0/cur_coeffs[2],
853 };
854
855 if(blackbody_is_confusing)
856 {
857 // show effect of adjustment on temp/tint sliders
858 for(int i = 0; i < DT_BAUHAUS_SLIDER_MAX_STOPS; i++)
859 {
860 const float stop = i / (DT_BAUHAUS_SLIDER_MAX_STOPS - 1.0);
861 const double K = DT_IOP_LOWEST_TEMPERATURE + i * temp_step;
862 const double tint = DT_IOP_LOWEST_TINT + i * tint_step;
863
864 double coeffs_K[4];
865 double coeffs_tint[4];
866 temp2mul(self, K, cur_tint, coeffs_K);
867 temp2mul(self, cur_temp, tint, coeffs_tint);
868 coeffs_K[0] /= coeffs_K[1];
869 coeffs_K[2] /= coeffs_K[1];
870 coeffs_K[3] /= coeffs_K[1];
871 coeffs_K[1] = 1.0;
872 coeffs_tint[0] /= coeffs_tint[1];
873 coeffs_tint[2] /= coeffs_tint[1];
874 coeffs_tint[3] /= coeffs_tint[1];
875 coeffs_tint[1] = 1.0;
876
877 dt_aligned_pixel_t sRGB_K = { dayligh_white[0]*coeffs_K[0], dayligh_white[1]*coeffs_K[1],
878 dayligh_white[2]*coeffs_K[2] };
879 dt_aligned_pixel_t sRGB_tint = {cur_white[0]*coeffs_tint[0], cur_white[1]*coeffs_tint[1],
880 cur_white[2]*coeffs_tint[2]};
881 const float maxsRGB_K = fmaxf(fmaxf(sRGB_K[0], sRGB_K[1]), sRGB_K[2]);
882 const float maxsRGB_tint = fmaxf(fmaxf(sRGB_tint[0], sRGB_tint[1]),sRGB_tint[2]);
883
884 if(maxsRGB_K > 1.f)
885 {
886 for(int ch=0; ch<3; ch++)
887 {
888 sRGB_K[ch] = fmaxf(sRGB_K[ch] / maxsRGB_K, 0.f);
889 }
890 }
891 if(maxsRGB_tint > 1.f)
892 {
893 for(int ch=0; ch<3; ch++)
894 {
895 sRGB_tint[ch] = fmaxf(sRGB_tint[ch] / maxsRGB_tint, 0.f);
896 }
897 }
898 dt_bauhaus_slider_set_stop(g->scale_k, stop, sRGB_K[0], sRGB_K[1], sRGB_K[2]);
899 dt_bauhaus_slider_set_stop(g->scale_tint, stop, sRGB_tint[0], sRGB_tint[1], sRGB_tint[2]);
900 }
901 }
902 else
903 {
904 // reflect actual black body colors for the temperature slider
905 for(int i = 0; i < DT_BAUHAUS_SLIDER_MAX_STOPS; i++)
906 {
907 const float stop = i / (DT_BAUHAUS_SLIDER_MAX_STOPS - 1.0);
908 const double K = DT_IOP_LOWEST_TEMPERATURE + i * temp_step;
909 const double tint = DT_IOP_LOWEST_TINT + i * tint_step;
910
911 const cmsCIEXYZ cmsXYZ_temp = temperature_tint_to_XYZ(K,cur_tint);
912 const cmsCIEXYZ cmsXYZ_tint = temperature_tint_to_XYZ(cur_temp, tint);
913 dt_aligned_pixel_t XYZ_temp = {cmsXYZ_temp.X, cmsXYZ_temp.Y, cmsXYZ_temp.Z};
914 dt_aligned_pixel_t XYZ_tint = {cmsXYZ_tint.X, cmsXYZ_tint.Y, cmsXYZ_tint.Z};
915 dt_aligned_pixel_t sRGB_temp;
916 dt_aligned_pixel_t sRGB_tint;
917
918 dt_XYZ_to_Rec709_D65(XYZ_temp, sRGB_temp);
919 dt_XYZ_to_Rec709_D65(XYZ_tint, sRGB_tint);
920
921 const float maxsRGB_temp = fmaxf(fmaxf(sRGB_temp[0], sRGB_temp[1]), sRGB_temp[2]);
922 const float maxsRGB_tint = fmaxf(fmaxf(sRGB_tint[0], sRGB_tint[1]), sRGB_tint[2]);
923
924 if(maxsRGB_temp > 1.f)
925 {
926 for(int ch=0; ch<3; ch++)
927 {
928 sRGB_temp[ch] = fmaxf(sRGB_temp[ch] / maxsRGB_temp, 0.f);
929 }
930 }
931
932 if(maxsRGB_tint > 1.f)
933 {
934 for(int ch=0; ch<3; ch++)
935 {
936 sRGB_tint[ch] = fmaxf(sRGB_tint[ch] / maxsRGB_tint, 0.f);
937 }
938 }
939
940 dt_bauhaus_slider_set_stop(g->scale_k, stop, sRGB_temp[0], sRGB_temp[1], sRGB_temp[2]);
941 dt_bauhaus_slider_set_stop(g->scale_tint, stop, sRGB_tint[0], sRGB_tint[1], sRGB_tint[2]);
942 }
943 }
944
945 if(gtk_widget_get_visible(GTK_WIDGET(g->scale_k)))
946 {
947 gtk_widget_queue_draw(GTK_WIDGET(g->scale_k));
948 gtk_widget_queue_draw(GTK_WIDGET(g->scale_tint));
949 }
950}
951
952
953void gui_update(struct dt_iop_module_t *self)
954{
957
958 const gboolean monochrome = dt_image_is_monochrome(&self->dev->image_storage);
959 const gboolean is_raw = dt_image_is_matrix_correction_supported(&self->dev->image_storage);
960 self->hide_enable_button = monochrome;
961 self->default_enabled = is_raw;
962 gtk_stack_set_visible_child_name(GTK_STACK(self->widget), self->hide_enable_button ? "disabled" : "enabled");
963
964// if(self->hide_enable_button) return;
965
967
968 float tempK, tint;
969 mul2temp(self, p, &tempK, &tint);
970
971 dt_bauhaus_slider_set(g->scale_k, tempK);
972 dt_bauhaus_slider_set(g->scale_tint, tint);
973 dt_bauhaus_slider_set(g->scale_r, p->red);
974 dt_bauhaus_slider_set(g->scale_g, p->green);
975 dt_bauhaus_slider_set(g->scale_b, p->blue);
976 dt_bauhaus_slider_set(g->scale_g2, p->g2);
977
978 dt_bauhaus_combobox_set(g->presets, -1);
979
980 gboolean found = FALSE;
981
982 // is this a "as shot" white balance?
983 if(p->red == g->as_shot_wb[0] && p->green == g->as_shot_wb[1] && p->blue == g->as_shot_wb[2])
984 {
986 found = TRUE;
987 }
988 else
989 {
990 // is this a "D65 white balance"?
991 if((p->red == (float)g->daylight_wb[0]) &&
992 (p->green == (float)g->daylight_wb[1]) &&
993 (p->blue == (float)g->daylight_wb[2]))
994 {
996 found = TRUE;
997 }
998 }
999
1000 if(!found)
1002
1003 if (!found || isnan(g->mod_temp)) // reset or initialize user-defined
1004 {
1005 g->mod_temp = tempK;
1006 g->mod_tint = tint;
1007 _temp_array_from_params(g->mod_coeff, p);
1008 }
1009
1010 gtk_widget_set_visible(g->buttonbar, g->button_bar_visible);
1011
1012 const int preset = dt_bauhaus_combobox_get(g->presets);
1013
1014 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(g->btn_asshot), preset == DT_IOP_TEMP_AS_SHOT);
1015 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(g->btn_user), preset == DT_IOP_TEMP_USER);
1016 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(g->btn_d65), preset == DT_IOP_TEMP_D65);
1017
1019 color_rgb_sliders(self);
1020
1022
1023 gtk_widget_queue_draw(self->widget);
1024}
1025
1026static int calculate_bogus_daylight_wb(dt_iop_module_t *module, double bwb[4])
1027{
1029 {
1030 bwb[0] = 1.0;
1031 bwb[2] = 1.0;
1032 bwb[1] = 1.0;
1033 bwb[3] = 1.0;
1034
1035 return 0;
1036 }
1037
1038 double mul[4];
1040 NULL, NULL,
1041 module->dev->image_storage.d65_color_matrix, mul))
1042 {
1043 // normalize green:
1044 bwb[0] = mul[0] / mul[1];
1045 bwb[2] = mul[2] / mul[1];
1046 bwb[1] = 1.0;
1047 bwb[3] = mul[3] / mul[1];
1048
1049 return 0;
1050 }
1051
1052 return 1;
1053}
1054
1056{
1058
1059 // sRGB D65
1060 const double RGB_to_XYZ[3][4] = { { 0.4124564, 0.3575761, 0.1804375, 0 },
1061 { 0.2126729, 0.7151522, 0.0721750, 0 },
1062 { 0.0193339, 0.1191920, 0.9503041, 0 } };
1063
1064 // sRGB D65
1065 const double XYZ_to_RGB[4][3] = { { 3.2404542, -1.5371385, -0.4985314 },
1066 { -0.9692660, 1.8760108, 0.0415560 },
1067 { 0.0556434, -0.2040259, 1.0572252 },
1068 { 0, 0, 0 } };
1069
1071 {
1072 // let's just assume for now(TM) that if it has no raw colorimetry, it is sRGB.
1073 // Gate on the colorimetry axis (raw or sRAW), not on the mosaic-only "raw" flag, otherwise
1074 // an already-demosaiced raw (sRAW / linear DNG) would skip its camera matrix here (issue #729).
1075 memcpy(g->XYZ_to_CAM, XYZ_to_RGB, sizeof(g->XYZ_to_CAM));
1076 memcpy(g->CAM_to_XYZ, RGB_to_XYZ, sizeof(g->CAM_to_XYZ));
1077 return;
1078 }
1079
1082 g->XYZ_to_CAM, g->CAM_to_XYZ))
1083 {
1084 char *camera = module->dev->image_storage.camera_makermodel;
1085 fprintf(stderr, "[temperature] `%s' color matrix not found for image\n", camera);
1086 dt_control_log(_("`%s' color matrix not found for image"), camera);
1087 }
1088}
1089
1090static void find_coeffs(dt_iop_module_t *module, double coeffs[4])
1091{
1092 const dt_image_t *img = &module->dev->image_storage;
1093
1094 // the raw should provide wb coeffs:
1095 int ok = 1;
1096 // Only check the first three values, the fourth is usually NAN for RGB
1097 const int num_coeffs = (img->flags & DT_IMAGE_4BAYER) ? 4 : 3;
1098 for(int k = 0; ok && k < num_coeffs; k++)
1099 {
1100 if(!isnormal(img->wb_coeffs[k]) || img->wb_coeffs[k] == 0.0f) ok = 0;
1101 }
1102 if(ok)
1103 {
1104 for(int k = 0; k < 4; k++) coeffs[k] = img->wb_coeffs[k];
1105 // wb_coeffs[3] is the SECOND green multiplier. For non-4-colour sensors (standard RGGB Bayer,
1106 // X-Trans) both green sites physically share the first green's multiplier, and many decoders
1107 // legitimately leave wb_coeffs[3] as NaN or 0 (see the "usually NAN for RGB" note above, and
1108 // the rawspeed DngDecoder which only fills 3 planes). Mirror coeffs[1] when the fourth value
1109 // is not a usable number, so we never propagate NaN into the G2 sites of the CFA.
1110 if(!(img->flags & DT_IMAGE_4BAYER) && !isnormal(coeffs[3])) coeffs[3] = coeffs[1];
1111 return;
1112 }
1113
1114 if(!ignore_missing_wb(&(module->dev->image_storage)))
1115 {
1116 // only display this if we have a sample, otherwise it is better to keep
1117 // on screen the more important message about missing sample and the way
1118 // to contribute.
1119 if(!img->camera_missing_sample)
1120 dt_control_log(_("failed to read camera white balance information from `%s'!"),
1121 img->filename);
1122 fprintf(stderr, "[temperature] failed to read camera white balance information from `%s'!\n",
1123 img->filename);
1124 }
1125
1126 double bwb[4];
1127 if(!calculate_bogus_daylight_wb(module, bwb))
1128 {
1129 // found camera matrix and used it to calculate bogus daylight wb
1130 for(int c = 0; c < 4; c++) coeffs[c] = bwb[c];
1131 return;
1132 }
1133
1134 // did not find preset either?
1135 // final security net: hardcoded default that fits most cams.
1136 coeffs[0] = 2.0;
1137 coeffs[1] = 1.0;
1138 coeffs[2] = 1.5;
1139 coeffs[3] = 1.0;
1140}
1141
1143{
1144 dt_iop_temperature_params_t *d = module->default_params;
1145 *d = (dt_iop_temperature_params_t){ 1.0, 1.0, 1.0, 1.0 };
1146
1147 // we might be called from presets update infrastructure => there is no image
1148 if(!module->dev || module->dev->image_storage.id == -1) return;
1149
1150 const gboolean is_raw = dt_image_is_matrix_correction_supported(&module->dev->image_storage);
1151 const gboolean monochrome = dt_image_is_monochrome(&module->dev->image_storage);
1152
1153 // Note : this is not an user param anymore, and is set to modern by default
1154 // except for old histories using temperature without having an history entry for it.
1155 // see develop/develop.c/_dev_auto_apply_presets()
1156 const gboolean is_modern =
1157 dt_conf_is_equal("plugins/darkroom/chromatic-adaptation", "modern");
1158
1159 module->default_enabled = 0;
1160 module->hide_enable_button = 0;
1161
1162 // White balance module doesn't need to be enabled for monochrome raws (like
1163 // for leica monochrom cameras). prepare_matrices is a noop as well, as there
1164 // isn't a color matrix, so we can skip that as well.
1165 if(monochrome)
1166 {
1167 module->hide_enable_button = 1;
1168 }
1169 else
1170 {
1171 if(module->gui_data) prepare_matrices(module);
1172
1173 /* check if file is raw / hdr */
1174 if(is_raw)
1175 {
1176 // raw images need wb:
1177 module->default_enabled = 1;
1178
1179 // if workflow = modern, only set WB coeffs equivalent to D65 illuminant
1180 // full chromatic adaptation is deferred to channelmixerrgb
1181 double coeffs[4] = { 0 };
1182 if(is_modern && !calculate_bogus_daylight_wb(module, coeffs))
1183 {
1184 d->red = coeffs[0]/coeffs[1];
1185 d->blue = coeffs[2]/coeffs[1];
1186 d->g2 = coeffs[3]/coeffs[1];
1187 d->green = 1.0f;
1188 }
1189 else
1190 {
1191 // do best to find starting coeffs
1192 find_coeffs(module, coeffs);
1193 d->red = coeffs[0]/coeffs[1];
1194 d->blue = coeffs[2]/coeffs[1];
1195 d->g2 = coeffs[3]/coeffs[1];
1196 d->green = 1.0f;
1197 }
1198 }
1199 }
1200
1201 dt_iop_fmt_log(module, "reload_defaults: class=%s matrix_supported=%d mono=%d modern=%d -> default_enabled=%d coeffs=[%.4f %.4f %.4f %.4f]",
1203 is_raw, monochrome, is_modern, module->default_enabled, d->red, d->green, d->blue, d->g2);
1204
1205 // remember daylight wb used for temperature/tint conversion,
1206 // assuming it corresponds to CIE daylight (D65)
1208 if(!IS_NULL_PTR(g))
1209 {
1210 gtk_stack_set_visible_child_name(GTK_STACK(module->widget), module->hide_enable_button ? "disabled" : "enabled");
1211
1212 dt_bauhaus_slider_set_default(g->scale_r, d->red);
1213 dt_bauhaus_slider_set_default(g->scale_g, d->green);
1214 dt_bauhaus_slider_set_default(g->scale_b, d->blue);
1215 dt_bauhaus_slider_set_default(g->scale_g2, d->g2);
1216
1217 // to have at least something and definitely not crash
1218 _temp_array_from_params(g->daylight_wb, d);
1219
1220 calculate_bogus_daylight_wb(module, g->daylight_wb);
1221
1222 // Store EXIF WB coeffs
1223 if(is_raw)
1224 find_coeffs(module, g->as_shot_wb);
1225 else
1226 g->as_shot_wb[0] = g->as_shot_wb[1] = g->as_shot_wb[2] = g->as_shot_wb[3] = 1.f;
1227
1228 g->as_shot_wb[0] /= g->as_shot_wb[1];
1229 g->as_shot_wb[2] /= g->as_shot_wb[1];
1230 g->as_shot_wb[3] /= g->as_shot_wb[1];
1231 g->as_shot_wb[1] = 1.0;
1232
1233 float TempK, tint;
1234 mul2temp(module, d, &TempK, &tint);
1235
1236 dt_bauhaus_slider_set_default(g->scale_k, TempK);
1237 dt_bauhaus_slider_set_default(g->scale_tint, tint);
1238
1239 dt_bauhaus_combobox_clear(g->presets);
1240
1241 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"
1242 dt_bauhaus_combobox_add(g->presets, C_("white balance", "from image area")); // old "spot", reason: describes exactly what'll happen
1243 dt_bauhaus_combobox_add(g->presets, C_("white balance", "user modified"));
1244 dt_bauhaus_combobox_add(g->presets, C_("white balance", "camera reference")); // old "camera neutral", reason: better matches intent
1245
1246 g->preset_cnt = DT_IOP_NUM_OF_STD_TEMP_PRESETS;
1247 memset(g->preset_num, 0, sizeof(g->preset_num));
1248
1249 gui_sliders_update(module);
1250 }
1251}
1252
1254{
1255 const int program = 2; // basic.cl, from programs.conf
1258 module->data = gd;
1259 gd->kernel_whitebalance_4f = dt_opencl_create_kernel(program, "whitebalance_4f");
1260 gd->kernel_whitebalance_1f = dt_opencl_create_kernel(program, "whitebalance_1f");
1261 gd->kernel_whitebalance_1f_xtrans = dt_opencl_create_kernel(program, "whitebalance_1f_xtrans");
1262}
1263
1272
1274{
1275 if(darktable.gui->reset) return;
1276
1279
1281
1282 g->mod_temp = dt_bauhaus_slider_get(g->scale_k);
1283 g->mod_tint = dt_bauhaus_slider_get(g->scale_tint);
1284
1285 temp2mul(self, g->mod_temp, g->mod_tint, g->mod_coeff);
1286
1287 // normalize
1288 g->mod_coeff[0] /= g->mod_coeff[1];
1289 g->mod_coeff[2] /= g->mod_coeff[1];
1290 g->mod_coeff[3] /= g->mod_coeff[1];
1291 g->mod_coeff[1] = 1.0;
1292
1293 _temp_params_from_array(p, g->mod_coeff);
1294
1295 ++darktable.gui->reset;
1296 dt_bauhaus_slider_set(g->scale_r, p->red);
1297 dt_bauhaus_slider_set(g->scale_g, p->green);
1298 dt_bauhaus_slider_set(g->scale_b, p->blue);
1299 dt_bauhaus_slider_set(g->scale_g2, p->g2);
1301 --darktable.gui->reset;
1302
1304}
1305
1306void gui_changed(dt_iop_module_t *self, GtkWidget *w, void *previous)
1307{
1310
1311 _temp_array_from_params(g->mod_coeff, p);
1312
1313 mul2temp(self, p, &g->mod_temp, &g->mod_tint);
1314
1316}
1317
1318static gboolean btn_toggled(GtkWidget *togglebutton, GdkEventButton *event, dt_iop_module_t *self)
1319{
1320 if(darktable.gui->reset) return TRUE;
1321
1323
1324 int preset = togglebutton == g->btn_asshot ? DT_IOP_TEMP_AS_SHOT :
1325 togglebutton == g->btn_d65 ? DT_IOP_TEMP_D65 :
1326 togglebutton == g->btn_user ? DT_IOP_TEMP_USER : 0;
1327
1328 if(!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(togglebutton)))
1329 {
1330 if(dt_bauhaus_combobox_get(g->presets) != preset)
1331 dt_bauhaus_combobox_set(g->presets, preset);
1332 }
1333 else if(dt_bauhaus_combobox_get(g->presets) == preset)
1334 {
1335 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(togglebutton), TRUE);
1336 }
1337
1338 return TRUE;
1339}
1340
1342{
1343 if(darktable.gui->reset) return;
1344
1347
1348 const int pos = dt_bauhaus_combobox_get(g->presets);
1349
1350 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(g->btn_asshot), pos == DT_IOP_TEMP_AS_SHOT);
1352 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(g->btn_user), pos == DT_IOP_TEMP_USER);
1353 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(g->btn_d65), pos == DT_IOP_TEMP_D65);
1354
1355 switch(pos)
1356 {
1357 case -1: // just un-setting.
1358 return;
1359 case DT_IOP_TEMP_AS_SHOT: // as shot wb
1360 _temp_params_from_array(p, g->as_shot_wb);
1361 break;
1362 case DT_IOP_TEMP_SPOT: // from image area wb, expose callback will set p->rgbg2.
1363 if(!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(g->colorpicker)))
1364 {
1365 gboolean ret_val;
1366 g_signal_emit_by_name(G_OBJECT(g->colorpicker), "button-press-event", NULL, &ret_val);
1367 }
1368 break;
1369 case DT_IOP_TEMP_USER: // directly changing one of the coeff sliders also changes the mod_coeff so it can be read here
1370 _temp_params_from_array(p, g->mod_coeff);
1371 break;
1372 case DT_IOP_TEMP_D65: // camera reference d65
1373 _temp_params_from_array(p, g->daylight_wb);
1374 break;
1375 default: // camera WB presets
1376 break;
1377 }
1378
1379 if(self->off) gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(self->off), 1);
1380
1381 float TempK, tint;
1382
1383 if(pos == DT_IOP_TEMP_USER)
1384 {
1385 TempK = g->mod_temp;
1386 tint = g->mod_tint;
1387 }
1388 else
1389 {
1390 mul2temp(self, p, &TempK, &tint);
1391 }
1392
1393 ++darktable.gui->reset;
1394 dt_bauhaus_slider_set(g->scale_k, TempK);
1395 dt_bauhaus_slider_set(g->scale_tint, tint);
1396 dt_bauhaus_slider_set(g->scale_r, p->red);
1397 dt_bauhaus_slider_set(g->scale_g, p->green);
1398 dt_bauhaus_slider_set(g->scale_b, p->blue);
1399 dt_bauhaus_slider_set(g->scale_g2, p->g2);
1400 --darktable.gui->reset;
1401
1403 color_rgb_sliders(self);
1404
1406}
1407
1409{
1410 (void)picker;
1411 (void)pipe;
1412 (void)piece;
1413 if(darktable.gui->reset) return;
1414
1417
1418 // capture gui color picked event.
1419 if(self->picked_color_max[0] < self->picked_color_min[0]) return;
1420 const float *grayrgb = self->picked_color;
1421
1422 // normalize green:
1423 p->green = grayrgb[1] > 0.001f ? 1.0f / grayrgb[1] : 1.0f;
1424 p->red = fmaxf(0.0f, fminf(8.0f, (grayrgb[0] > 0.001f ? 1.0f / grayrgb[0] : 1.0f) / p->green));
1425 p->blue = fmaxf(0.0f, fminf(8.0f, (grayrgb[2] > 0.001f ? 1.0f / grayrgb[2] : 1.0f) / p->green));
1426 p->g2 = fmaxf(0.0f, fminf(8.0f, (grayrgb[3] > 0.001f ? 1.0f / grayrgb[3] : 1.0f) / p->green));
1427 p->green = 1.0;
1428
1429 ++darktable.gui->reset;
1431
1432 float tempK, tint;
1433 mul2temp(self, p, &tempK, &tint);
1434
1435 dt_bauhaus_slider_set(g->scale_k, tempK);
1436 dt_bauhaus_slider_set(g->scale_tint, tint);
1437 dt_bauhaus_slider_set(g->scale_r, p->red);
1438 dt_bauhaus_slider_set(g->scale_g, p->green);
1439 dt_bauhaus_slider_set(g->scale_b, p->blue);
1440 dt_bauhaus_slider_set(g->scale_g2, p->g2);
1441
1442 dt_bauhaus_combobox_set(g->presets, -1);
1443 --darktable.gui->reset;
1444
1445 dt_dev_add_history_item(self->dev, self, TRUE, TRUE);
1446}
1447
1448
1449static void gui_sliders_update(struct dt_iop_module_t *self)
1450{
1451 const dt_image_t *img = &self->dev->image_storage;
1453
1454 if(FILTERS_ARE_CYGM(img->dsc.filters))
1455 {
1456 dt_bauhaus_widget_set_label(g->scale_r, N_("green"));
1457 gtk_widget_set_tooltip_text(g->scale_r, _("green channel coefficient"));
1458 dt_bauhaus_widget_set_label(g->scale_g, N_("magenta"));
1459 gtk_widget_set_tooltip_text(g->scale_g, _("magenta channel coefficient"));
1460 dt_bauhaus_widget_set_label(g->scale_b, N_("cyan"));
1461 gtk_widget_set_tooltip_text(g->scale_b, _("cyan channel coefficient"));
1462 dt_bauhaus_widget_set_label(g->scale_g2, N_("yellow"));
1463 gtk_widget_set_tooltip_text(g->scale_g2, _("yellow channel coefficient"));
1464
1465 gtk_box_reorder_child(GTK_BOX(g->cs.container), g->scale_b, 0);
1466 gtk_box_reorder_child(GTK_BOX(g->cs.container), g->scale_g2, 1);
1467 gtk_box_reorder_child(GTK_BOX(g->cs.container), g->scale_g, 2);
1468 gtk_box_reorder_child(GTK_BOX(g->cs.container), g->scale_r, 3);
1469 }
1470 else
1471 {
1472 dt_bauhaus_widget_set_label(g->scale_r, N_("red"));
1473 gtk_widget_set_tooltip_text(g->scale_r, _("red channel coefficient"));
1474 dt_bauhaus_widget_set_label(g->scale_g, N_("green"));
1475 gtk_widget_set_tooltip_text(g->scale_g, _("green channel coefficient"));
1476 dt_bauhaus_widget_set_label(g->scale_b, N_("blue"));
1477 gtk_widget_set_tooltip_text(g->scale_b, _("blue channel coefficient"));
1478 dt_bauhaus_widget_set_label(g->scale_g2, N_("emerald"));
1479 gtk_widget_set_tooltip_text(g->scale_g2, _("emerald channel coefficient"));
1480
1481 gtk_box_reorder_child(GTK_BOX(g->cs.container), g->scale_r, 0);
1482 gtk_box_reorder_child(GTK_BOX(g->cs.container), g->scale_g, 1);
1483 gtk_box_reorder_child(GTK_BOX(g->cs.container), g->scale_b, 2);
1484 gtk_box_reorder_child(GTK_BOX(g->cs.container), g->scale_g2, 3);
1485 }
1486
1487 gtk_widget_set_visible(GTK_WIDGET(g->scale_g2), (img->flags & DT_IMAGE_4BAYER));
1488}
1489
1490void gui_init(struct dt_iop_module_t *self)
1491{
1493
1494 g->colored_sliders = TRUE; // true if config != "no color"
1495 g->blackbody_is_confusing = FALSE; // true if config != "illuminant color"
1496
1497 const int feedback = TRUE;
1498 g->button_bar_visible = TRUE;
1499
1500 GtkBox *box_enabled = GTK_BOX(gtk_box_new(GTK_ORIENTATION_VERTICAL, DT_GUI_BOX_SPACING));
1501
1502 g->btn_asshot = dt_iop_togglebutton_new(self, N_("settings"), N_("as shot"), NULL,
1503 G_CALLBACK(btn_toggled), FALSE, 0, 0,
1505 gtk_widget_set_tooltip_text(g->btn_asshot, _("set white balance to as shot"));
1506
1507 // create color picker to be able to send its signal when spot selected,
1508 // this module may expect data in RAW or RGB, setting the color picker CST to IOP_CS_NONE will make the color
1509 // picker to depend on the number of color channels of the pixels. It is done like this as we may not know the
1510 // actual kind of data we are using in the GUI (it is part of the pipeline).
1513
1514 gtk_widget_set_tooltip_text(g->colorpicker, _("set white balance to detected from area"));
1515
1516 g->btn_user = dt_iop_togglebutton_new(self, N_("settings"), N_("user modified"), NULL,
1517 G_CALLBACK(btn_toggled), FALSE, 0, 0,
1519 gtk_widget_set_tooltip_text(g->btn_user, _("set white balance to user modified"));
1520
1521
1522 g->btn_d65 = dt_iop_togglebutton_new(self, N_("settings"), N_("camera reference"), NULL,
1523 G_CALLBACK(btn_toggled), FALSE, 0, 0,
1525 gtk_widget_set_tooltip_text(g->btn_d65, _("set white balance to camera reference point\nin most cases it should be D65"));
1526
1527 g->buttonbar = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, DT_GUI_BOX_SPACING); // put buttons at top. fill later.
1528 dt_gui_add_class(g->buttonbar, "dt_iop_toggle");
1529 gtk_box_pack_end(GTK_BOX(g->buttonbar), g->btn_d65, TRUE, TRUE, 0);
1530 gtk_box_pack_end(GTK_BOX(g->buttonbar), g->btn_user, TRUE, TRUE, 0);
1531 gtk_box_pack_end(GTK_BOX(g->buttonbar), g->colorpicker, TRUE, TRUE, 0);
1532 gtk_box_pack_end(GTK_BOX(g->buttonbar), g->btn_asshot, TRUE, TRUE, 0);
1533 gtk_box_pack_start(box_enabled, g->buttonbar, TRUE, TRUE, 0);
1534
1536 dt_bauhaus_widget_set_label(g->presets, N_("settings")); // relabel to settings to remove confusion between module presets and white balance settings
1537 gtk_widget_set_tooltip_text(g->presets, _("choose white balance setting"));
1538 gtk_box_pack_start(box_enabled, g->presets, TRUE, TRUE, 0);
1539
1540 g->mod_temp = NAN;
1541 for(int k = 0; k < 4; k++)
1542 {
1543 g->daylight_wb[k] = 1.0;
1544 g->as_shot_wb[k] = 1.f;
1545 }
1546
1547 GtkWidget *temp_label_box = gtk_event_box_new();
1548 g->temp_label = dt_ui_section_label_new(_("scene illuminant temp"));
1549 gtk_widget_set_tooltip_text(g->temp_label, _("click to cycle color mode on sliders"));
1550 gtk_container_add(GTK_CONTAINER(temp_label_box), g->temp_label);
1551
1552 gtk_box_pack_start(box_enabled, temp_label_box, TRUE, TRUE, 0);
1553
1554 //Match UI order: temp first, then tint (like every other app ever)
1556 0, 5000.0, 0, feedback);
1557 dt_bauhaus_slider_set_format(g->scale_k, " K");
1558 dt_bauhaus_widget_set_label(g->scale_k, N_("temperature"));
1559 gtk_widget_set_tooltip_text(g->scale_k, _("color temperature (in Kelvin)"));
1560 gtk_box_pack_start(box_enabled, g->scale_k, TRUE, TRUE, 0);
1561
1563 0, 1.0, 3, feedback);
1564 dt_bauhaus_widget_set_label(g->scale_tint, N_("tint"));
1565 gtk_widget_set_tooltip_text(g->scale_tint, _("color tint of the image, from magenta (value < 1) to green (value > 1)"));
1566 gtk_box_pack_start(box_enabled, g->scale_tint, TRUE, TRUE, 0);
1567
1569 (&g->cs,
1570 "plugins/darkroom/temperature/expand_coefficients",
1571 _("channel coefficients"),
1572 GTK_BOX(box_enabled), GTK_PACK_END);
1573
1574 self->widget = GTK_WIDGET(g->cs.container);
1575
1576 g->scale_r = dt_bauhaus_slider_from_params(self, N_("red"));
1577 g->scale_g = dt_bauhaus_slider_from_params(self, N_("green"));
1578 g->scale_b = dt_bauhaus_slider_from_params(self, N_("blue"));
1579 g->scale_g2 = dt_bauhaus_slider_from_params(self, "g2");
1580 dt_bauhaus_slider_set_digits(g->scale_r, 3);
1581 dt_bauhaus_slider_set_digits(g->scale_g, 3);
1582 dt_bauhaus_slider_set_digits(g->scale_b, 3);
1583 dt_bauhaus_slider_set_digits(g->scale_g2, 3);
1584
1585 gtk_widget_set_no_show_all(g->scale_g2, TRUE);
1586
1587 g_signal_connect(G_OBJECT(g->scale_k), "value-changed", G_CALLBACK(temp_tint_callback), self);
1588 g_signal_connect(G_OBJECT(g->scale_tint), "value-changed", G_CALLBACK(temp_tint_callback), self);
1589
1590 g_signal_connect(G_OBJECT(g->presets), "value-changed", G_CALLBACK(preset_tune_callback), self);
1591
1592 // start building top level widget
1593 self->widget = gtk_stack_new();
1594 gtk_stack_set_homogeneous(GTK_STACK(self->widget), FALSE);
1595
1596 GtkWidget *label_disabled = gtk_label_new(_("white balance disabled for camera"));
1597 gtk_widget_set_halign(label_disabled, GTK_ALIGN_START);
1598 gtk_label_set_ellipsize(GTK_LABEL(label_disabled), PANGO_ELLIPSIZE_END);
1599
1600 gtk_stack_add_named(GTK_STACK(self->widget), GTK_WIDGET(box_enabled), "enabled");
1601 gtk_stack_add_named(GTK_STACK(self->widget), label_disabled, "disabled");
1602}
1603
1605{
1607
1609}
1610
1611void gui_reset(struct dt_iop_module_t *self)
1612{
1614
1615 const int preset = dt_bauhaus_combobox_get(g->presets);
1617
1618 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(g->btn_asshot), preset == DT_IOP_TEMP_AS_SHOT);
1619 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(g->btn_user), preset == DT_IOP_TEMP_USER);
1620 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(g->btn_d65), preset == DT_IOP_TEMP_D65);
1621
1622 color_rgb_sliders(self);
1624}
1625
1626// clang-format off
1627// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
1628// vim: shiftwidth=2 expandtab tabstop=2 cindent
1629// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
1630// 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:3534
void dt_bauhaus_combobox_clear(GtkWidget *widget)
Definition bauhaus.c:2189
void dt_bauhaus_slider_clear_stops(GtkWidget *widget)
Definition bauhaus.c:2364
void dt_bauhaus_slider_set_default(GtkWidget *widget, float def)
Definition bauhaus.c:1640
void dt_bauhaus_slider_set_stop(GtkWidget *widget, float stop, float r, float g, float b)
Definition bauhaus.c:2372
float dt_bauhaus_slider_get(GtkWidget *widget)
Definition bauhaus.c:3483
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:1786
void dt_bauhaus_slider_set_feedback(GtkWidget *widget, int feedback)
Definition bauhaus.c:3580
int dt_bauhaus_combobox_get(GtkWidget *widget)
Definition bauhaus.c:2347
void dt_bauhaus_slider_set(GtkWidget *widget, float pos)
Definition bauhaus.c:3506
void dt_bauhaus_combobox_set(GtkWidget *widget, const int pos)
Definition bauhaus.c:2301
void dt_bauhaus_widget_set_label(GtkWidget *widget, const char *label)
Definition bauhaus.c:1653
GtkWidget * dt_bauhaus_combobox_new(dt_bauhaus_t *bh, dt_gui_module_t *self)
Definition bauhaus.c:1842
void dt_bauhaus_slider_set_format(GtkWidget *widget, const char *format)
Definition bauhaus.c:3598
float dt_bauhaus_slider_get_hard_max(GtkWidget *widget)
Definition bauhaus.c:1601
void dt_bauhaus_combobox_add(GtkWidget *widget, const char *text)
Definition bauhaus.c:2016
#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:761
darktable_t darktable
Definition darktable.c:181
void dt_print(dt_debug_thread_t thread, const char *msg,...)
Definition darktable.c:1542
#define dt_free_align(ptr)
Definition darktable.h:481
static void * dt_calloc_align(size_t size)
Definition darktable.h:488
@ DT_DEBUG_OPENCL
Definition darktable.h:722
#define dt_free(ptr)
Definition darktable.h:456
#define DT_MODULE_INTROSPECTION(MODVER, PARAMSTYPE)
Definition darktable.h:151
#define __DT_CLONE_TARGETS__
Definition darktable.h:367
#define __OMP_PARALLEL_FOR__(...)
Definition darktable.h:258
#define IS_NULL_PTR(p)
C is way too permissive with !=, == and if(var) checks, which can mean too many things depending on w...
Definition darktable.h:281
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:41
@ 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:3102
void dt_gui_update_collapsible_section(dt_gui_collapsible_section_t *cs)
Definition gtk.c:3080
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:451
#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:3141
@ DT_REQUEST_COLORPICK_OFF
Definition imageop.h:196
#define IOP_GUI_FREE
Definition imageop.h:602
#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:453
@ IOP_FLAGS_ALLOW_TILING
Definition imageop.h:169
@ IOP_FLAGS_ONE_INSTANCE
Definition imageop.h:172
@ IOP_GROUP_TECHNICAL
Definition imageop.h:143
#define IOP_GUI_ALLOC(module)
Definition imageop.h:599
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:2136
int dt_opencl_create_kernel(const int prog, const char *name)
Definition opencl.c:2030
void * dt_opencl_copy_host_to_device_constant(const int devid, const size_t size, void *host)
Definition opencl.c:2332
void dt_opencl_free_kernel(const int kernel)
Definition opencl.c:2073
int dt_opencl_set_kernel_arg(const int dev, const int kernel, const int num, const size_t size, const void *arg)
Definition opencl.c:2127
void dt_opencl_release_mem_object(cl_mem mem)
Definition opencl.c:2383
#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_gui_gtk_t * gui
Definition darktable.h:775
struct dt_bauhaus_t * bauhaus
Definition darktable.h:778
struct dt_develop_t * develop
Definition darktable.h:770
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:441
dt_aligned_pixel_t wb_coeffs
Definition develop.h:442
struct dt_develop_t::@20 proxy
int32_t reset
Definition gtk.h:172
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
dt_aligned_pixel_t coeffs
Definition format.h:81
uint32_t filters
Definition format.h:60
struct dt_iop_buffer_dsc_t::@30 temperature
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:233
dt_dev_request_colorpick_flags_t request_color_pick
Definition imageop.h:264
GtkDarktableToggleButton * off
Definition imageop.h:339
int32_t hide_enable_button
Definition imageop.h:262
GtkWidget * widget
Definition imageop.h:337
struct dt_develop_t * dev
Definition imageop.h:296
dt_iop_gui_data_t * gui_data
Definition imageop.h:311
gboolean default_enabled
Definition imageop.h:303
dt_iop_global_data_t * global_data
Definition imageop.h:314
dt_aligned_pixel_t picked_color_min
Definition imageop.h:272
dt_aligned_pixel_t picked_color_max
Definition imageop.h:272
dt_aligned_pixel_t picked_color
Definition imageop.h:272
dt_iop_params_t * params
Definition imageop.h:307
Region of interest passed through the pixelpipe.
Definition imageop.h:72
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)
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)
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:87
void dtgtk_togglebutton_set_paint(GtkDarktableToggleButton *button, DTGTKCairoPaintIconFunc paint, gint paintflags, void *paintdata)
#define DTGTK_TOGGLEBUTTON(obj)