temperature.c 50.6 KB
Newer Older
1 2
/*
    This file is part of darktable,
3
    copyright (c) 2009--2013 johannes hanika.
4
    copyright (c) 2015 LebedevRI.
5
    copyright (c) 2016 Pedro Côrte-Real
6 7 8 9 10 11 12 13 14 15 16 17 18 19

    darktable is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    darktable is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with darktable.  If not, see <http://www.gnu.org/licenses/>.
*/
20
#ifdef HAVE_CONFIG_H
21
#include "config.h"
22
#endif
23
#if defined(__SSE__)
24
#include <xmmintrin.h>
25
#endif
26
#include <assert.h>
27
#include <lcms2.h>
28 29 30
#include <math.h>
#include <stdlib.h>
#include <string.h>
31

32
#include "bauhaus/bauhaus.h"
33
#include "common/colorspaces_inline_conversions.h"
34
#include "common/darktable.h"
35 36
#include "common/opencl.h"
#include "control/control.h"
37
#include "develop/develop.h"
38
#include "develop/imageop_math.h"
39
#include "develop/tiling.h"
40
#include "external/wb_presets.c"
41
#include "gui/accelerators.h"
42
#include "gui/gtk.h"
43
#include "iop/iop_api.h"
44

45 46
// for Kelvin temperature and bogus WB
#include "common/colorspaces.h"
47
#include "external/cie_colorimetric_tables.c"
48

49
DT_MODULE_INTROSPECTION(3, dt_iop_temperature_params_t)
50

51 52 53 54 55 56 57
#define INITIALBLACKBODYTEMPERATURE 4000

#define DT_IOP_LOWEST_TEMPERATURE 1901
#define DT_IOP_HIGHEST_TEMPERATURE 25000

#define DT_IOP_LOWEST_TINT 0.135
#define DT_IOP_HIGHEST_TINT 2.326
58 59

#define DT_IOP_NUM_OF_STD_TEMP_PRESETS 3
60

61 62
#define COLORED_SLIDERS 0

63 64
static void gui_sliders_update(struct dt_iop_module_t *self);

65 66
typedef struct dt_iop_temperature_params_t
{
67
  float coeffs[4];
68
} dt_iop_temperature_params_t;
69 70 71

typedef struct dt_iop_temperature_gui_data_t
{
72
  GtkWidget *scale_k, *scale_tint, *coeff_widgets, *scale_r, *scale_g, *scale_b, *scale_g2;
73 74 75 76
  GtkWidget *presets;
  GtkWidget *finetune;
  int preset_cnt;
  int preset_num[50];
77 78
  double daylight_wb[4];
  double XYZ_to_CAM[4][3], CAM_to_XYZ[3][4];
79
} dt_iop_temperature_gui_data_t;
80 81 82

typedef struct dt_iop_temperature_data_t
{
83
  float coeffs[4];
84
} dt_iop_temperature_data_t;
85 86


87 88 89
typedef struct dt_iop_temperature_global_data_t
{
  int kernel_whitebalance_4f;
90
  int kernel_whitebalance_1f;
91
  int kernel_whitebalance_1f_xtrans;
92
} dt_iop_temperature_global_data_t;
93

94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117
int legacy_params(dt_iop_module_t *self, const void *const old_params, const int old_version,
                  void *new_params, const int new_version)
{
  if(old_version == 2 && new_version == 3)
  {
    typedef struct dt_iop_temperature_params_v2_t
    {
      float temp_out;
      float coeffs[3];
    } dt_iop_temperature_params_v2_t;

    dt_iop_temperature_params_v2_t *o = (dt_iop_temperature_params_v2_t *)old_params;
    dt_iop_temperature_params_t *n = (dt_iop_temperature_params_t *)new_params;

    n->coeffs[0] = o->coeffs[0];
    n->coeffs[1] = o->coeffs[1];
    n->coeffs[2] = o->coeffs[2];
    n->coeffs[3] = NAN;

    return 0;
  }
  return 1;
}

118 119 120 121 122 123 124 125 126 127 128 129
static int is_leica_monochrom(dt_image_t *img)
{
  if(strncmp(img->exif_maker, "Leica Camera AG", 15) != 0) return 0;

  gchar *tmp_model = g_ascii_strdown(img->exif_model, -1);

  const int res = strstr(tmp_model, "monochrom") != NULL;
  g_free(tmp_model);

  return res;
}

130 131 132 133 134 135 136
static int ignore_missing_wb(dt_image_t *img)
{
  // Ignore files that end with "-hdr.dng" since these are broken files we
  // generated without any proper WB tagged
  if(g_str_has_suffix(img->filename,"-hdr.dng"))
    return TRUE;

137
  static const char *const ignored_cameras[] = {
138 139 140 141 142 143 144 145 146 147 148 149 150
    "Canon PowerShot A610",
    "Canon PowerShot S3 IS",
    "Canon PowerShot A620",
    "Canon PowerShot A720 IS",
    "Canon PowerShot A630",
    "Canon PowerShot A640",
    "Canon PowerShot A650",
    "Canon PowerShot SX110 IS",
    "Mamiya ZD",
    "Canon EOS D2000C",
    "Kodak EOS DCS 1",
    "Kodak DCS560C",
    "Kodak DCS460D",
151
    "Nikon E5700",
152
    "Sony DSC-F828",
153
    "GITUP GIT2",
154 155 156 157 158 159 160 161 162
  };

  for(int i=0; i < sizeof(ignored_cameras)/sizeof(ignored_cameras[1]); i++)
    if(!strcmp(img->camera_makermodel, ignored_cameras[i]))
      return TRUE;

  return FALSE;
}

johannes hanika's avatar
johannes hanika committed
163 164
const char *name()
{
Alexandre Prokoudine's avatar
Alexandre Prokoudine committed
165
  return C_("modulename", "white balance");
johannes hanika's avatar
johannes hanika committed
166
}
167

168

169
int groups()
170
{
171
  return IOP_GROUP_BASIC;
172 173
}

174
int flags()
175
{
176
  return IOP_FLAGS_ALLOW_TILING | IOP_FLAGS_ONE_INSTANCE;
177 178
}

179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205
static gboolean _set_preset_camera(GtkAccelGroup *accel_group, GObject *acceleratable, guint keyval,
                                   GdkModifierType modifier, gpointer data)
{
  dt_iop_module_t *self = data;
  dt_iop_temperature_gui_data_t *g = self->gui_data;
  dt_bauhaus_combobox_set(g->presets, 0);
  return TRUE;
}

static gboolean _set_preset_camera_neutral(GtkAccelGroup *accel_group, GObject *acceleratable, guint keyval,
                                           GdkModifierType modifier, gpointer data)
{
  dt_iop_module_t *self = data;
  dt_iop_temperature_gui_data_t *g = self->gui_data;
  dt_bauhaus_combobox_set(g->presets, 1);
  return TRUE;
}

static gboolean _set_preset_spot(GtkAccelGroup *accel_group, GObject *acceleratable, guint keyval,
                                 GdkModifierType modifier, gpointer data)
{
  dt_iop_module_t *self = data;
  dt_iop_temperature_gui_data_t *g = self->gui_data;
  dt_bauhaus_combobox_set(g->presets, 2);
  return TRUE;
}

206 207 208
void init_key_accels(dt_iop_module_so_t *self)
{
  dt_accel_register_slider_iop(self, FALSE, NC_("accel", "tint"));
209
  dt_accel_register_slider_iop(self, FALSE, NC_("accel", "temperature"));
210 211 212
  dt_accel_register_slider_iop(self, FALSE, NC_("accel", "red"));
  dt_accel_register_slider_iop(self, FALSE, NC_("accel", "green"));
  dt_accel_register_slider_iop(self, FALSE, NC_("accel", "blue"));
213 214 215 216

  dt_accel_register_iop(self, TRUE, NC_("accel", "preset/camera"), 0, 0);
  dt_accel_register_iop(self, TRUE, NC_("accel", "preset/camera neutral"), 0, 0);
  dt_accel_register_iop(self, TRUE, NC_("accel", "preset/spot"), 0, 0);
217 218 219 220
}

void connect_key_accels(dt_iop_module_t *self)
{
221
  dt_iop_temperature_gui_data_t *g = (dt_iop_temperature_gui_data_t *)self->gui_data;
222 223

  dt_accel_connect_slider_iop(self, "tint", GTK_WIDGET(g->scale_tint));
224
  dt_accel_connect_slider_iop(self, "temperature", GTK_WIDGET(g->scale_k));
225 226 227
  dt_accel_connect_slider_iop(self, "red", GTK_WIDGET(g->scale_r));
  dt_accel_connect_slider_iop(self, "green", GTK_WIDGET(g->scale_g));
  dt_accel_connect_slider_iop(self, "blue", GTK_WIDGET(g->scale_b));
228
  dt_accel_connect_slider_iop(self, "green2", GTK_WIDGET(g->scale_g2));
229 230 231 232 233 234 235 236 237 238 239

  GClosure *closure;

  closure = g_cclosure_new(G_CALLBACK(_set_preset_camera), (gpointer)self, NULL);
  dt_accel_connect_iop(self, "preset/camera", closure);

  closure = g_cclosure_new(G_CALLBACK(_set_preset_camera_neutral), (gpointer)self, NULL);
  dt_accel_connect_iop(self, "preset/camera neutral", closure);

  closure = g_cclosure_new(G_CALLBACK(_set_preset_spot), (gpointer)self, NULL);
  dt_accel_connect_iop(self, "preset/spot", closure);
240 241
}

242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298
/*
 * Spectral power distribution functions
 * https://en.wikipedia.org/wiki/Spectral_power_distribution
 */
typedef double((*spd)(unsigned long int wavelength, double TempK));

/*
 * Bruce Lindbloom, "Spectral Power Distribution of a Blackbody Radiator"
 * http://www.brucelindbloom.com/Eqn_Blackbody.html
 */
static double spd_blackbody(unsigned long int wavelength, double TempK)
{
  // convert wavelength from nm to m
  const long double lambda = (double)wavelength * 1e-9;

/*
 * these 2 constants were computed using following Sage code:
 *
 * (from http://physics.nist.gov/cgi-bin/cuu/Value?h)
 * h = 6.62606957 * 10^-34 # Planck
 * c= 299792458 # speed of light in vacuum
 * k = 1.3806488 * 10^-23 # Boltzmann
 *
 * c_1 = 2 * pi * h * c^2
 * c_2 = h * c / k
 *
 * print 'c_1 = ', c_1, ' ~= ', RealField(128)(c_1)
 * print 'c_2 = ', c_2, ' ~= ', RealField(128)(c_2)
 */

#define c1 3.7417715246641281639549488324352159753e-16L
#define c2 0.014387769599838156481252937624049081933L

  return (double)(c1 / (powl(lambda, 5) * (expl(c2 / (lambda * TempK)) - 1.0L)));

#undef c2
#undef c1
}

/*
 * Bruce Lindbloom, "Spectral Power Distribution of a CIE D-Illuminant"
 * http://www.brucelindbloom.com/Eqn_DIlluminant.html
 * and https://en.wikipedia.org/wiki/Standard_illuminant#Illuminant_series_D
 */
static double spd_daylight(unsigned long int wavelength, double TempK)
{
  cmsCIExyY WhitePoint = { 0.3127, 0.3290, 1.0 };

  /*
   * Bruce Lindbloom, "TempK to xy"
   * http://www.brucelindbloom.com/Eqn_T_to_xy.html
   */
  cmsWhitePointFromTemp(&WhitePoint, TempK);

  const double M = (0.0241 + 0.2562 * WhitePoint.x - 0.7341 * WhitePoint.y),
               m1 = (-1.3515 - 1.7703 * WhitePoint.x + 5.9114 * WhitePoint.y) / M,
               m2 = (0.0300 - 31.4424 * WhitePoint.x + 30.0717 * WhitePoint.y) / M;
299

300 301 302 303 304 305 306 307 308 309 310 311 312
  const unsigned long int j
      = ((wavelength - cie_daylight_components[0].wavelength)
         / (cie_daylight_components[1].wavelength - cie_daylight_components[0].wavelength));

  return (cie_daylight_components[j].S[0] + m1 * cie_daylight_components[j].S[1]
          + m2 * cie_daylight_components[j].S[2]);
}

/*
 * Bruce Lindbloom, "Computing XYZ From Spectral Data (Emissive Case)"
 * http://www.brucelindbloom.com/Eqn_Spect_to_XYZ.html
 */
static cmsCIEXYZ spectrum_to_XYZ(double TempK, spd I)
313
{
314
  cmsCIEXYZ Source = {.X = 0.0, .Y = 0.0, .Z = 0.0 };
315

316 317 318
  /*
   * Color matching functions
   * https://en.wikipedia.org/wiki/CIE_1931_color_space#Color_matching_functions
319
   */
320
  for(size_t i = 0; i < cie_1931_std_colorimetric_observer_count; i++)
Tobias Ellinghaus's avatar
Tobias Ellinghaus committed
321
  {
322 323 324 325 326 327 328
    const unsigned long int lambda = cie_1931_std_colorimetric_observer[0].wavelength
                                     + (cie_1931_std_colorimetric_observer[1].wavelength
                                        - cie_1931_std_colorimetric_observer[0].wavelength) * i;
    const double P = I(lambda, TempK);
    Source.X += P * cie_1931_std_colorimetric_observer[i].xyz.X;
    Source.Y += P * cie_1931_std_colorimetric_observer[i].xyz.Y;
    Source.Z += P * cie_1931_std_colorimetric_observer[i].xyz.Z;
Tobias Ellinghaus's avatar
Tobias Ellinghaus committed
329
  }
330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346

  // normalize so that each component is in [0.0, 1.0] range
  const double _max = MAX(MAX(Source.X, Source.Y), Source.Z);
  Source.X /= _max;
  Source.Y /= _max;
  Source.Z /= _max;

  return Source;
}

//
static cmsCIEXYZ temperature_to_XYZ(double TempK)
{
  if(TempK < DT_IOP_LOWEST_TEMPERATURE) TempK = DT_IOP_LOWEST_TEMPERATURE;
  if(TempK > DT_IOP_HIGHEST_TEMPERATURE) TempK = DT_IOP_HIGHEST_TEMPERATURE;

  if(TempK < INITIALBLACKBODYTEMPERATURE)
Tobias Ellinghaus's avatar
Tobias Ellinghaus committed
347
  {
348 349 350
    // if temperature is less than 4000K we use blackbody,
    // because there will be no Daylight reference below 4000K...
    return spectrum_to_XYZ(TempK, spd_blackbody);
Tobias Ellinghaus's avatar
Tobias Ellinghaus committed
351 352 353
  }
  else
  {
354
    return spectrum_to_XYZ(TempK, spd_daylight);
355
  }
356
}
357

358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380
// binary search inversion
static void XYZ_to_temperature(cmsCIEXYZ XYZ, double *TempK, double *tint)
{
  double maxtemp = DT_IOP_HIGHEST_TEMPERATURE, mintemp = DT_IOP_LOWEST_TEMPERATURE;
  cmsCIEXYZ _xyz;

  for(*TempK = (maxtemp + mintemp) / 2.0; (maxtemp - mintemp) > 1.0; *TempK = (maxtemp + mintemp) / 2.0)
  {
    _xyz = temperature_to_XYZ(*TempK);
    if(_xyz.Z / _xyz.X > XYZ.Z / XYZ.X)
      maxtemp = *TempK;
    else
      mintemp = *TempK;
  }

  *tint = (_xyz.Y / _xyz.X) / (XYZ.Y / XYZ.X);

  if(*TempK < DT_IOP_LOWEST_TEMPERATURE) *TempK = DT_IOP_LOWEST_TEMPERATURE;
  if(*TempK > DT_IOP_HIGHEST_TEMPERATURE) *TempK = DT_IOP_HIGHEST_TEMPERATURE;
  if(*tint < DT_IOP_LOWEST_TINT) *tint = DT_IOP_LOWEST_TINT;
  if(*tint > DT_IOP_HIGHEST_TINT) *tint = DT_IOP_HIGHEST_TINT;
}

381
static void xyz2mul(dt_iop_module_t *self, cmsCIEXYZ xyz, double mul[4])
382 383
{
  dt_iop_temperature_gui_data_t *g = (dt_iop_temperature_gui_data_t *)self->gui_data;
384

385
  double XYZ[3] = { xyz.X, xyz.Y, xyz.Z };
386

387 388
  double CAM[4];
  for(int k = 0; k < 4; k++)
Tobias Ellinghaus's avatar
Tobias Ellinghaus committed
389
  {
390
    CAM[k] = 0.0;
391 392
    for(int i = 0; i < 3; i++)
    {
393
      CAM[k] += g->XYZ_to_CAM[k][i] * XYZ[i];
394
    }
395
  }
396

397
  for(int k = 0; k < 4; k++) mul[k] = 1.0 / CAM[k];
398 399
}

400 401 402 403 404 405 406 407 408 409
static void temp2mul(dt_iop_module_t *self, double TempK, double tint, double mul[4])
{
  cmsCIEXYZ xyz = temperature_to_XYZ(TempK);

  xyz.Y /= tint;

  xyz2mul(self, xyz, mul);
}

static cmsCIEXYZ mul2xyz(dt_iop_module_t *self, const float coeffs[4])
410
{
411 412
  dt_iop_temperature_gui_data_t *g = (dt_iop_temperature_gui_data_t *)self->gui_data;

413
  double CAM[4];
414
  for(int k = 0; k < 4; k++) CAM[k] = coeffs[k] > 0.0f ? 1.0 / coeffs[k] : 0.0f;
415 416 417

  double XYZ[3];
  for(int k = 0; k < 3; k++)
418
  {
419
    XYZ[k] = 0.0;
420
    for(int i = 0; i < 4; i++)
421
    {
422
      XYZ[k] += g->CAM_to_XYZ[k][i] * CAM[i];
423
    }
424
  }
425

426 427 428 429 430 431
  return (cmsCIEXYZ){ XYZ[0], XYZ[1], XYZ[2] };
}

static void mul2temp(dt_iop_module_t *self, float coeffs[4], double *TempK, double *tint)
{
  XYZ_to_temperature(mul2xyz(self, coeffs), TempK, tint);
432 433
}

434 435 436
/*
 * interpolate values from p1 and p2 into out.
 */
437 438 439
static void dt_wb_preset_interpolate(const wb_data *const p1, // the smaller tuning
                                     const wb_data *const p2, // the larger tuning (can't be == p1)
                                     wb_data *out)            // has tuning initialized
440 441 442 443
{
  const double t = CLAMP((double)(out->tuning - p1->tuning) / (double)(p2->tuning - p1->tuning), 0.0, 1.0);
  for(int k = 0; k < 3; k++)
  {
444
    out->channel[k] = 1.0 / (((1.0 - t) / p1->channel[k]) + (t / p2->channel[k]));
445 446 447
  }
}

448 449 450
void process(struct dt_iop_module_t *self, dt_dev_pixelpipe_iop_t *piece, const void *const ivoid,
             void *const ovoid, const dt_iop_roi_t *const roi_in, const dt_iop_roi_t *const roi_out)
{
451 452
  const uint32_t filters = piece->pipe->dsc.filters;
  const uint8_t(*const xtrans)[6] = (const uint8_t(*const)[6])piece->pipe->dsc.xtrans;
453 454 455 456 457
  const dt_iop_temperature_data_t *const d = (dt_iop_temperature_data_t *)piece->data;

  const float *const in = (const float *const)ivoid;
  float *const out = (float *const)ovoid;

458
  if(filters == 9u)
459 460
  { // xtrans float mosaiced
#ifdef _OPENMP
461
#pragma omp parallel for SIMD() default(none) schedule(static) collapse(2)
462 463 464 465 466 467 468 469 470 471
#endif
    for(int j = 0; j < roi_out->height; j++)
    {
      for(int i = 0; i < roi_out->width; i++)
      {
        const size_t p = (size_t)j * roi_out->width + i;
        out[p] = in[p] * d->coeffs[FCxtrans(j, i, roi_out, xtrans)];
      }
    }
  }
472
  else if(filters)
473 474
  { // bayer float mosaiced
#ifdef _OPENMP
475
#pragma omp parallel for SIMD() default(none) schedule(static) collapse(2)
476 477 478 479 480 481 482 483 484 485 486 487 488 489 490
#endif
    for(int j = 0; j < roi_out->height; j++)
    {
      for(int i = 0; i < roi_out->width; i++)
      {
        const size_t p = (size_t)j * roi_out->width + i;
        out[p] = in[p] * d->coeffs[FC(j + roi_out->y, i + roi_out->x, filters)];
      }
    }
  }
  else
  { // non-mosaiced
    const int ch = piece->colors;

#ifdef _OPENMP
491
#pragma omp parallel for SIMD() default(none) schedule(static) collapse(2)
492 493 494 495 496 497 498 499 500 501
#endif
    for(size_t k = 0; k < (size_t)ch * roi_out->width * roi_out->height; k += ch)
    {
      for(int c = 0; c < 3; c++)
      {
        const size_t p = (size_t)k + c;
        out[p] = in[p] * d->coeffs[c];
      }
    }

502
    if(piece->pipe->mask_display & DT_DEV_PIXELPIPE_DISPLAY_MASK) dt_iop_alpha_copy(ivoid, ovoid, roi_out->width, roi_out->height);
503
  }
504 505

  piece->pipe->dsc.temperature.enabled = 1;
506
  for(int k = 0; k < 4; k++)
507 508
  {
    piece->pipe->dsc.temperature.coeffs[k] = d->coeffs[k];
509
    piece->pipe->dsc.processed_maximum[k] = d->coeffs[k] * piece->pipe->dsc.processed_maximum[k];
510
  }
511 512 513
}

#if defined(__SSE__)
514 515
void process_sse2(struct dt_iop_module_t *self, dt_dev_pixelpipe_iop_t *piece, const void *const ivoid,
                  void *const ovoid, const dt_iop_roi_t *const roi_in, const dt_iop_roi_t *const roi_out)
516
{
517 518
  const uint32_t filters = piece->pipe->dsc.filters;
  const uint8_t(*const xtrans)[6] = (const uint8_t(*const)[6])piece->pipe->dsc.xtrans;
519
  dt_iop_temperature_data_t *d = (dt_iop_temperature_data_t *)piece->data;
520
  if(filters == 9u)
521
  { // xtrans float mosaiced
522
#ifdef _OPENMP
523
#pragma omp parallel for default(none) shared(d) schedule(static)
524
#endif
525
    for(int j = 0; j < roi_out->height; j++)
526
    {
527 528
      const float *in = ((float *)ivoid) + (size_t)j * roi_out->width;
      float *out = ((float *)ovoid) + (size_t)j * roi_out->width;
529 530 531 532 533

      int i = 0;
      int alignment = ((4 - (j * roi_out->width & (4 - 1))) & (4 - 1));

      // process unaligned pixels
534 535
      for(; i < alignment && i < roi_out->width; i++, out++, in++)
        *out = *in * d->coeffs[FCxtrans(j, i, roi_out, xtrans)];
536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557

      const __m128 coeffs[3] = {
        _mm_set_ps(d->coeffs[FCxtrans(j, i + 3, roi_out, xtrans)], d->coeffs[FCxtrans(j, i + 2, roi_out, xtrans)],
                   d->coeffs[FCxtrans(j, i + 1, roi_out, xtrans)], d->coeffs[FCxtrans(j, i + 0, roi_out, xtrans)]),
        _mm_set_ps(d->coeffs[FCxtrans(j, i + 7, roi_out, xtrans)], d->coeffs[FCxtrans(j, i + 6, roi_out, xtrans)],
                   d->coeffs[FCxtrans(j, i + 5, roi_out, xtrans)], d->coeffs[FCxtrans(j, i + 4, roi_out, xtrans)]),
        _mm_set_ps(d->coeffs[FCxtrans(j, i + 11, roi_out, xtrans)], d->coeffs[FCxtrans(j, i + 10, roi_out, xtrans)],
                   d->coeffs[FCxtrans(j, i + 9, roi_out, xtrans)], d->coeffs[FCxtrans(j, i + 8, roi_out, xtrans)])
      };

      // process aligned pixels with SSE
      for(int c = 0; c < 3 && i < roi_out->width - (4 - 1); c++, i += 4, in += 4, out += 4)
      {
        __m128 v;

        v = _mm_load_ps(in);
        v = _mm_mul_ps(v, coeffs[c]);
        _mm_stream_ps(out, v);
      }

      // process the rest
      for(; i < roi_out->width; i++, out++, in++) *out = *in * d->coeffs[FCxtrans(j, i, roi_out, xtrans)];
558
    }
559
    _mm_sfence();
Dan Torop's avatar
Dan Torop committed
560
  }
561
  else if(filters)
562
  { // bayer float mosaiced
563
#ifdef _OPENMP
564
#pragma omp parallel for default(none) shared(d) schedule(static)
565
#endif
566
    for(int j = 0; j < roi_out->height; j++)
Bruce Guenter's avatar
Bruce Guenter committed
567
    {
568 569
      const float *in = ((float *)ivoid) + (size_t)j * roi_out->width;
      float *out = ((float *)ovoid) + (size_t)j * roi_out->width;
570

571 572
      int i = 0;
      int alignment = ((4 - (j * roi_out->width & (4 - 1))) & (4 - 1));
Roman Lebedev's avatar
Roman Lebedev committed
573

574
      // process unaligned pixels
575
      for(; i < alignment && i < roi_out->width; i++, out++, in++)
576
        *out = *in * d->coeffs[FC(j + roi_out->y, i + roi_out->x, filters)];
577

578 579 580 581
      const __m128 coeffs = _mm_set_ps(d->coeffs[FC(j + roi_out->y, roi_out->x + i + 3, filters)],
                                       d->coeffs[FC(j + roi_out->y, roi_out->x + i + 2, filters)],
                                       d->coeffs[FC(j + roi_out->y, roi_out->x + i + 1, filters)],
                                       d->coeffs[FC(j + roi_out->y, roi_out->x + i, filters)]);
582 583

      // process aligned pixels with SSE
584
      for(; i < roi_out->width - (4 - 1); i += 4, in += 4, out += 4)
585
      {
586
        const __m128 input = _mm_load_ps(in);
Roman Lebedev's avatar
Roman Lebedev committed
587

588
        const __m128 multiplied = _mm_mul_ps(input, coeffs);
Roman Lebedev's avatar
Roman Lebedev committed
589

590
        _mm_stream_ps(out, multiplied);
591 592 593
      }

      // process the rest
594 595
      for(; i < roi_out->width; i++, out++, in++)
        *out = *in * d->coeffs[FC(j + roi_out->y, i + roi_out->x, filters)];
596
    }
597
    _mm_sfence();
598
  }
599 600 601
  else
  { // non-mosaiced
    const int ch = piece->colors;
602

603
    const __m128 coeffs = _mm_set_ps(1.0f, d->coeffs[2], d->coeffs[1], d->coeffs[0]);
604

605
#ifdef _OPENMP
606
#pragma omp parallel for default(none) shared(d) schedule(static)
607
#endif
608
    for(int k = 0; k < roi_out->height; k++)
Bruce Guenter's avatar
Bruce Guenter committed
609
    {
610 611 612
      const float *in = ((float *)ivoid) + (size_t)ch * k * roi_out->width;
      float *out = ((float *)ovoid) + (size_t)ch * k * roi_out->width;
      for(int j = 0; j < roi_out->width; j++, in += ch, out += ch)
613 614 615 616 617
      {
        const __m128 input = _mm_load_ps(in);
        const __m128 multiplied = _mm_mul_ps(input, coeffs);
        _mm_stream_ps(out, multiplied);
      }
Bruce Guenter's avatar
Bruce Guenter committed
618
    }
619 620
    _mm_sfence();

621
    if(piece->pipe->mask_display & DT_DEV_PIXELPIPE_DISPLAY_MASK) dt_iop_alpha_copy(ivoid, ovoid, roi_out->width, roi_out->height);
622
  }
623 624

  piece->pipe->dsc.temperature.enabled = 1;
625
  for(int k = 0; k < 4; k++)
626 627
  {
    piece->pipe->dsc.temperature.coeffs[k] = d->coeffs[k];
628
    piece->pipe->dsc.processed_maximum[k] = d->coeffs[k] * piece->pipe->dsc.processed_maximum[k];
629
  }
630
}
631
#endif
632

633
#ifdef HAVE_OPENCL
634
int process_cl(struct dt_iop_module_t *self, dt_dev_pixelpipe_iop_t *piece, cl_mem dev_in, cl_mem dev_out,
635
               const dt_iop_roi_t *const roi_in, const dt_iop_roi_t *const roi_out)
636 637 638 639 640
{
  dt_iop_temperature_data_t *d = (dt_iop_temperature_data_t *)piece->data;
  dt_iop_temperature_global_data_t *gd = (dt_iop_temperature_global_data_t *)self->data;

  const int devid = piece->pipe->devid;
641
  const uint32_t filters = piece->pipe->dsc.filters;
642
  cl_mem dev_coeffs = NULL;
643
  cl_mem dev_xtrans = NULL;
644
  cl_int err = -999;
645
  int kernel = -1;
646

647 648 649 650 651
  if(filters == 9u)
  {
    kernel = gd->kernel_whitebalance_1f_xtrans;
  }
  else if(filters)
652 653 654 655 656 657 658
  {
    kernel = gd->kernel_whitebalance_1f;
  }
  else
  {
    kernel = gd->kernel_whitebalance_4f;
  }
659

660 661 662 663 664 665 666
  if(filters == 9u)
  {
    dev_xtrans
        = dt_opencl_copy_host_to_device_constant(devid, sizeof(piece->pipe->dsc.xtrans), piece->pipe->dsc.xtrans);
    if(dev_xtrans == NULL) goto error;
  }

667 668
  dev_coeffs = dt_opencl_copy_host_to_device_constant(devid, sizeof(float) * 3, d->coeffs);
  if(dev_coeffs == NULL) goto error;
669

670 671 672
  const int width = roi_in->width;
  const int height = roi_in->height;

673
  size_t sizes[] = { ROUNDUPWD(width), ROUNDUPHT(height), 1 };
674 675
  dt_opencl_set_kernel_arg(devid, kernel, 0, sizeof(cl_mem), (void *)&dev_in);
  dt_opencl_set_kernel_arg(devid, kernel, 1, sizeof(cl_mem), (void *)&dev_out);
676 677 678
  dt_opencl_set_kernel_arg(devid, kernel, 2, sizeof(int), (void *)&width);
  dt_opencl_set_kernel_arg(devid, kernel, 3, sizeof(int), (void *)&height);
  dt_opencl_set_kernel_arg(devid, kernel, 4, sizeof(cl_mem), (void *)&dev_coeffs);
679 680 681
  dt_opencl_set_kernel_arg(devid, kernel, 5, sizeof(uint32_t), (void *)&filters);
  dt_opencl_set_kernel_arg(devid, kernel, 6, sizeof(uint32_t), (void *)&roi_out->x);
  dt_opencl_set_kernel_arg(devid, kernel, 7, sizeof(uint32_t), (void *)&roi_out->y);
682
  dt_opencl_set_kernel_arg(devid, kernel, 8, sizeof(cl_mem), (void *)&dev_xtrans);
683
  err = dt_opencl_enqueue_kernel_2d(devid, kernel, sizes);
684
  if(err != CL_SUCCESS) goto error;
685

686
  dt_opencl_release_mem_object(dev_coeffs);
687
  dt_opencl_release_mem_object(dev_xtrans);
688 689

  piece->pipe->dsc.temperature.enabled = 1;
690
  for(int k = 0; k < 4; k++)
691 692
  {
    piece->pipe->dsc.temperature.coeffs[k] = d->coeffs[k];
693
    piece->pipe->dsc.processed_maximum[k] = d->coeffs[k] * piece->pipe->dsc.processed_maximum[k];
694
  }
695 696 697
  return TRUE;

error:
698 699
  dt_opencl_release_mem_object(dev_coeffs);
  dt_opencl_release_mem_object(dev_xtrans);
700 701
  dt_print(DT_DEBUG_OPENCL, "[opencl_white_balance] couldn't enqueue kernel! %d\n", err);
  return FALSE;
702 703 704
}
#endif

705 706
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)
707 708 709
{
  dt_iop_temperature_params_t *p = (dt_iop_temperature_params_t *)p1;
  dt_iop_temperature_data_t *d = (dt_iop_temperature_data_t *)piece->data;
710
  for(int k = 0; k < 4; k++) d->coeffs[k] = p->coeffs[k];
Dan Torop's avatar
Dan Torop committed
711

712 713
  // 4Bayer images not implemented in OpenCL yet
  if(self->dev->image_storage.flags & DT_IMAGE_4BAYER) piece->process_cl_ready = 0;
714 715
}

716
void init_pipe(struct dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
717 718 719 720 721
{
  piece->data = malloc(sizeof(dt_iop_temperature_data_t));
  self->commit_params(self, self->default_params, pipe, piece);
}

722
void cleanup_pipe(struct dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
723 724
{
  free(piece->data);
jhanika's avatar
jhanika committed
725
  piece->data = NULL;
726 727
}

728
void gui_update(struct dt_iop_module_t *self)
729 730
{
  dt_iop_module_t *module = (dt_iop_module_t *)self;
731
  self->request_color_pick = DT_REQUEST_COLORPICK_OFF;
732 733
  self->color_picker_box[0] = self->color_picker_box[1] = .25f;
  self->color_picker_box[2] = self->color_picker_box[3] = .75f;
734
  self->color_picker_point[0] = self->color_picker_point[1] = 0.5f;
735
  dt_iop_temperature_gui_data_t *g = (dt_iop_temperature_gui_data_t *)self->gui_data;
736
  dt_iop_temperature_params_t *p = (dt_iop_temperature_params_t *)module->params;
737
  dt_iop_temperature_params_t *fp = (dt_iop_temperature_params_t *)module->default_params;
738 739 740

  double TempK, tint;
  mul2temp(self, p->coeffs, &TempK, &tint);
741

742 743 744
  dt_bauhaus_slider_set(g->scale_r, p->coeffs[0]);
  dt_bauhaus_slider_set(g->scale_g, p->coeffs[1]);
  dt_bauhaus_slider_set(g->scale_b, p->coeffs[2]);
745
  dt_bauhaus_slider_set(g->scale_g2, p->coeffs[3]);
746
  dt_bauhaus_slider_set(g->scale_k, TempK);
747
  dt_bauhaus_slider_set(g->scale_tint, tint);
748

749 750
  gui_sliders_update(self);

751
  dt_bauhaus_combobox_clear(g->presets);
752 753 754
  dt_bauhaus_combobox_add(g->presets, C_("white balance", "camera"));
  dt_bauhaus_combobox_add(g->presets, C_("white balance", "camera neutral"));
  dt_bauhaus_combobox_add(g->presets, C_("white balance", "spot"));
755
  g->preset_cnt = DT_IOP_NUM_OF_STD_TEMP_PRESETS;
756
  memset(g->preset_num, 0, sizeof(g->preset_num));
757

758 759 760 761
  dt_bauhaus_combobox_set(g->presets, -1);
  dt_bauhaus_slider_set(g->finetune, 0);
  gtk_widget_set_sensitive(g->finetune, 0);

762
  const char *wb_name = NULL;
763 764
  if(!dt_image_is_ldr(&self->dev->image_storage))
    for(int i = 0; i < wb_preset_count; i++)
765 766
    {
      if(g->preset_cnt >= 50) break;
767 768
      if(!strcmp(wb_preset[i].make, self->dev->image_storage.camera_maker) 
         && !strcmp(wb_preset[i].model, self->dev->image_storage.camera_model))
769 770 771 772 773
      {
        if(!wb_name || strcmp(wb_name, wb_preset[i].name))
        {
          wb_name = wb_preset[i].name;
          dt_bauhaus_combobox_add(g->presets, _(wb_preset[i].name));
774 775
          g->preset_num[g->preset_cnt] = i;
          g->preset_cnt++;
776 777 778 779
        }
      }
    }

780 781
  gboolean found = FALSE;
  // is this a camera white balance?
782
  if(memcmp(p->coeffs, fp->coeffs, 3 * sizeof(float)) == 0)
783
  {
784
    dt_bauhaus_combobox_set(g->presets, 0);
785 786
    found = TRUE;
  }
787
  else
788
  {
789
    // is this a "camera neutral white balance"?
790 791
    if((p->coeffs[0] == (float)g->daylight_wb[0]) && (p->coeffs[1] == (float)g->daylight_wb[1])
       && (p->coeffs[2] == (float)g->daylight_wb[2]))
792 793 794 795 796 797 798 799
    {
      dt_bauhaus_combobox_set(g->presets, 1);
      found = TRUE;
    }
  }

  if(!found)
  {
800
    // look through all added presets
801
    for(int j = DT_IOP_NUM_OF_STD_TEMP_PRESETS; !found && (j < g->preset_cnt); j++)
802 803
    {
      // look through all variants of this preset, with different tuning
804 805 806 807
      for(int i = g->preset_num[j]; !found && (i < wb_preset_count) 
                                    && !strcmp(wb_preset[i].make, self->dev->image_storage.camera_maker)
                                    && !strcmp(wb_preset[i].model, self->dev->image_storage.camera_model)
                                    && !strcmp(wb_preset[i].name, wb_preset[g->preset_num[j]].name);
808 809 810 811 812 813 814 815 816 817 818
          i++)
      {
        float coeffs[3];
        for(int k = 0; k < 3; k++) coeffs[k] = wb_preset[i].channel[k];

        if(memcmp(coeffs, p->coeffs, 3 * sizeof(float)) == 0)
        {
          // got exact match!
          dt_bauhaus_combobox_set(g->presets, j);
          gtk_widget_set_sensitive(g->finetune, 1);
          dt_bauhaus_slider_set(g->finetune, wb_preset[i].tuning);
819
          found = TRUE;
820 821 822 823
          break;
        }
      }
    }
824 825 826 827 828 829 830 831 832 833

    if(!found)
    {
      // ok, we haven't found exact match, maybe this was interpolated?

      // look through all added presets
      for(int j = DT_IOP_NUM_OF_STD_TEMP_PRESETS; !found && (j < g->preset_cnt); j++)
      {
        // look through all variants of this preset, with different tuning
        int i = g->preset_num[j] + 1;
834 835
        while(!found && (i < wb_preset_count) && !strcmp(wb_preset[i].make, self->dev->image_storage.camera_maker)
              && !strcmp(wb_preset[i].model, self->dev->image_storage.camera_maker)
836 837 838
              && !strcmp(wb_preset[i].name, wb_preset[g->preset_num[j]].name))
        {
          // let's find gaps
839 840 841 842 843
          if(wb_preset[i - 1].tuning + 1 == wb_preset[i].tuning)
          {
            i++;
            continue;
          }
844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870

          // we have a gap!

          // we do not know what finetuning value was set, we need to bruteforce to find it
          for(int tune = wb_preset[i - 1].tuning + 1; !found && (tune < wb_preset[i].tuning); tune++)
          {
            wb_data interpolated = {.tuning = tune };
            dt_wb_preset_interpolate(&wb_preset[i - 1], &wb_preset[i], &interpolated);

            float coeffs[3];
            for(int k = 0; k < 3; k++) coeffs[k] = interpolated.channel[k];

            if(memcmp(coeffs, p->coeffs, 3 * sizeof(float)) == 0)
            {
              // got exact match!

              dt_bauhaus_combobox_set(g->presets, j);
              gtk_widget_set_sensitive(g->finetune, 1);
              dt_bauhaus_slider_set(g->finetune, tune);
              found = TRUE;
              break;
            }
          }
          i++;
        }
      }
    }
871
  }
872 873
}

874
static int calculate_bogus_daylight_wb(dt_iop_module_t *module, double bwb[4])
875
{
876 877 878 879 880
  if(!dt_image_is_raw(&module->dev->image_storage))
  {
    bwb[0] = 1.0;
    bwb[2] = 1.0;
    bwb[1] = 1.0;
881
    bwb[3] = 1.0;
882 883 884 885

    return 0;
  }

886 887
  double mul[4];
  if (dt_colorspaces_conversion_matrices_rgb(module->dev->image_storage.camera_makermodel, NULL, NULL, mul))
888
  {
889 890 891
    // normalize green:
    bwb[0] = mul[0] / mul[1];
    bwb[2] = mul[2] / mul[1];
892
    bwb[1] = 1.0;
893
    bwb[3] = mul[3] / mul[1];
894

895 896 897 898 899 900
    return 0;
  }

  return 1;
}

901
static void prepare_matrices(dt_iop_module_t *module)
902 903 904 905
{
  dt_iop_temperature_gui_data_t *g = (dt_iop_temperature_gui_data_t *)module->gui_data;

  // sRGB D65
906 907 908
  const double RGB_to_XYZ[3][4] = { { 0.4124564, 0.3575761, 0.1804375, 0 },
                                    { 0.2126729, 0.7151522, 0.0721750, 0 },
                                    { 0.0193339, 0.1191920, 0.9503041, 0 } };
909 910

  // sRGB D65
911
  const double XYZ_to_RGB[4][3] = { { 3.2404542, -1.5371385, -0.4985314 },
912
                                    { -0.9692660, 1.8760108, 0.0415560 },
913 914
                                    { 0.0556434, -0.2040259, 1.0572252 },
                                    { 0, 0, 0 } };
915 916 917 918 919 920

  if(!dt_image_is_raw(&module->dev->image_storage))
  {
    // let's just assume for now(TM) that if it is not raw, it is sRGB
    memcpy(g->XYZ_to_CAM, XYZ_to_RGB, sizeof(g->XYZ_to_CAM));
    memcpy(g->CAM_to_XYZ, RGB_to_XYZ, sizeof(g->CAM_to_XYZ));
921
    return;
922 923
  }

924 925 926 927
  char *camera = module->dev->image_storage.camera_makermodel;
  if (!dt_colorspaces_conversion_matrices_xyz(camera, module->dev->image_storage.d65_color_matrix,
                                                      g->XYZ_to_CAM, g->CAM_to_XYZ))
  {
928
    fprintf(stderr, "[temperature] `%s' color matrix not found for image\n", camera);
929
    dt_control_log(_("`%s' color matrix not found for image"), camera);
930
  }
931 932
}

933
void reload_defaults(dt_iop_module_t *module)
934
{
935
  dt_iop_temperature_params_t tmp
936
      = (dt_iop_temperature_params_t){.coeffs = { 1.0, 1.0, 1.0, 1.0 } };
937 938 939 940

  // we might be called from presets update infrastructure => there is no image
  if(!module->dev) goto end;

941
  if(module->gui_data) prepare_matrices(module);
942

943
  /* check if file is raw / hdr */
944
  if(dt_image_is_raw(&module->dev->image_storage))
945
  {
946
    // raw images need wb:
947
    module->default_enabled = 1;
948

949
    int found = 1;
950

951
    // Only check the first three values, the fourth is usually NAN for RGB
952 953
    int num_coeffs = (module->dev->image_storage.flags & DT_IMAGE_4BAYER) ? 4 : 3;
    for(int k = 0; k < num_coeffs; k++)
954
    {
955
      if(!isnormal(module->dev->image_storage.wb_coeffs[k]) || module->dev->image_storage.wb_coeffs[k] == 0.0f)
956
      {
957 958
        found = 0;
        break;
959
      }
960
    }
961

962 963
    if(found)
    {
964
      for(int k = 0; k < 4; k++) tmp.coeffs[k] = module->dev->image_storage.wb_coeffs[k];
965 966 967
    }
    else
    {
968
      if(!is_leica_monochrom(&(module->dev->image_storage)))
969
      {
970 971
        if(!ignore_missing_wb(&(module->dev->image_storage)))
        {
972 973
          dt_control_log(_("failed to read camera white balance information from `%s'!"),
                         module->dev->image_storage.filename);
974 975 976
          fprintf(stderr, "[temperature] failed to read camera white balance information from `%s'!\n",
                  module->dev->image_storage.filename);
        }
977
      }
978
      else
979
      {
980
        // nop white balance is valid for monochrome sraws (like the leica monochrom produces)
981
        goto gui;
982
      }
983 984
    }

985
    if(!found)
986
    {
987
      double bwb[4];
988
      if(!calculate_bogus_daylight_wb(module, bwb))
989 990
      {
        // found camera matrix and used it to calculate bogus daylight wb
991
        for(int c = 0; c < 4; c++) tmp.coeffs[c] = bwb[c];
992 993
        found = 1;
      }
994 995
    }

996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013
    // no cam matrix??? try presets:
    if(!found)
    {
      for(int i = 0; i < wb_preset_count; i++)
      {
        if(!strcmp(wb_preset[i].make, module->dev->image_storage.camera_maker)
           && !strcmp(wb_preset[i].model, module->dev->image_storage.camera_model))
        {
          // just take the first preset we find for this camera
          for(int k = 0; k < 3; k++) tmp.coeffs[k] = wb_preset[i].channel[k];
          found = 1;
          break;
        }
      }
    }

    // did not find preset either?
    if(!found)
1014 1015 1016 1017 1018
    {
      // final security net: hardcoded default that fits most cams.
      tmp.coeffs[0] = 2.0f;
      tmp.coeffs[1] = 1.0f;
      tmp.coeffs[2] = 1.5f;
1019
      tmp.coeffs[3] = 1.0f;
1020 1021
    }

1022 1023
    tmp.coeffs[0] /= tmp.coeffs[1];
    tmp.coeffs[2] /= tmp.coeffs[1];
1024
    tmp.coeffs[3] /= tmp.coeffs[1];
1025
    tmp.coeffs[1] = 1.0f;
1026
  }
1027

1028
gui:
1029 1030 1031 1032 1033
  // remember daylight wb used for temperature/tint conversion,
  // assuming it corresponds to CIE daylight (D65)
  if(module->gui_data)
  {
    dt_iop_temperature_gui_data_t *g = (dt_iop_temperature_gui_data_t *)module->gui_data;
1034

1035 1036 1037
    dt_bauhaus_slider_set_default(g->scale_r, tmp.coeffs[0]);
    dt_bauhaus_slider_set_default(g->scale_g, tmp.coeffs[1]);
    dt_bauhaus_slider_set_default(g->scale_b, tmp.coeffs[2]);
1038
    dt_bauhaus_slider_set_default(g->scale_g2, tmp.coeffs[3]);
1039

1040
    // to have at least something and definitely not crash
1041
    for(int c = 0; c < 4; c++) g->daylight_wb[c] = tmp.coeffs[c];
1042

1043 1044 1045 1046 1047 1048 1049 1050 1051
    if(!calculate_bogus_daylight_wb(module, g->daylight_wb))
    {
      // found camera matrix and used it to calculate bogus daylight wb
    }
    else
    {
      // if we didn't find anything for daylight wb, look for a wb preset with appropriate name.
      // we're normalizing that to be D65
      for(int i = 0; i < wb_preset_count; i++)
1052
      {
1053 1054
        if(!strcmp(wb_preset[i].make, module->dev->image_storage.camera_maker) 
           && !strcmp(wb_preset[i].model, module->dev->image_storage.camera_model)
1055
           && !strcmp(wb_preset[i].name, Daylight) && wb_preset[i].tuning == 0)
1056
        {
1057
          for(int k = 0; k < 4; k++) g->daylight_wb[k] = wb_preset[i].channel[k];
1058
          break;
1059 1060
        }
      }
1061
    }
1062

1063 1064
    double TempK, tint;
    mul2temp(module, tmp.coeffs, &TempK, &tint);
1065

1066 1067
    dt_bauhaus_slider_set_default(g->scale_k, TempK);
    dt_bauhaus_slider_set_default(g->scale_tint, tint);
1068

1069
#if COLORED_SLIDERS
1070 1071 1072 1073 1074
    const float neutral_stop_tint = (tint - DT_IOP_LOWEST_TINT) / (DT_IOP_HIGHEST_TINT - DT_IOP_LOWEST_TINT);
    dt_bauhaus_slider_clear_stops(g->scale_tint);
    dt_bauhaus_slider_set_stop(g->scale_tint, 0.0, 1.0, 0.0, 1.0);
    dt_bauhaus_slider_set_stop(g->scale_tint, neutral_stop_tint, 1.0, 1.0, 1.0);
    dt_bauhaus_slider_set_stop(g->scale_tint, 1.0, 0.0, 1.0, 0.0);
1075
#endif
1076
  }
1077

1078
end:
1079 1080 1081 1082
  memcpy(module->params, &tmp, sizeof(dt_iop_temperature_params_t));
  memcpy(module->default_params, &tmp, sizeof(dt_iop_temperature_params_t));
}

1083 1084 1085
void init_global(dt_iop_module_so_t *module)
{
  const int program = 2; // basic.cl, from programs.conf
1086 1087
  dt_iop_temperature_global_data_t *gd
      = (dt_iop_temperature_global_data_t *)malloc(sizeof(dt_iop_temperature_global_data_t));
1088
  module->data = gd;
1089 1090
  gd->kernel_whitebalance_4f = dt_opencl_create_kernel(program, "whitebalance_4f");
  gd->kernel_whitebalance_1f = dt_opencl_create_kernel(program, "whitebalance_1f");
1091
  gd->kernel_whitebalance_1f_xtrans = dt_opencl_create_kernel(program, "whitebalance_1f_xtrans");
1092 1093
}

1094
void init(dt_iop_module_t *module)
1095
{
1096 1097
  module->params = calloc(1, sizeof(dt_iop_temperature_params_t));
  module->default_params = calloc(1, sizeof(dt_iop_temperature_params_t));
Roman Lebedev's avatar
Roman Lebedev committed
1098
  module->priority = 44; // module order created by iop_dependencies.py, do not edit!
1099 1100 1101 1102
  module->params_size = sizeof(dt_iop_temperature_params_t);
  module->gui_data = NULL;
}

1103
void cleanup(dt_iop_module_t *module)
1104 1105 1106 1107 1108
{
  free(module->params);
  module->params = NULL;
}

1109 1110 1111
void cleanup_global(dt_iop_module_so_t *module)
{
  dt_iop_temperature_global_data_t *gd = (dt_iop_temperature_global_data_t *)module->data;
1112
  dt_opencl_free_kernel(gd->kernel_whitebalance_4f);
1113
  dt_opencl_free_kernel(gd->kernel_whitebalance_1f);
1114
  dt_opencl_free_kernel(gd->kernel_whitebalance_1f_xtrans);
1115 1116 1117 1118
  free(module->data);
  module->data = NULL;
}

1119
static void gui_update_from_coeffs(dt_iop_module_t *self)
1120 1121
{
  dt_iop_temperature_gui_data_t *g = (dt_iop_temperature_gui_data_t *)self->gui_data;
1122
  dt_iop_temperature_params_t *p = (dt_iop_temperature_params_t *)self->params;
1123

1124 1125
  double TempK, tint;
  mul2temp(self, p->coeffs, &TempK, &tint);
1126 1127

  darktable.gui->reset = 1;
1128
  dt_bauhaus_slider_set(g->scale_k, TempK);
1129 1130 1131 1132
  dt_bauhaus_slider_set(g->scale_tint, tint);
  dt_bauhaus_slider_set(g->scale_r, p->coeffs[0]);
  dt_bauhaus_slider_set(g->scale_g, p->coeffs[1]);
  dt_bauhaus_slider_set(g->scale_b, p->coeffs[2]);
1133
  dt_bauhaus_slider_set(g->scale_g2, p->coeffs[3]);
1134 1135 1136 1137
  darktable.gui->reset = 0;
}


1138
static gboolean draw(GtkWidget *widget, cairo_t *cr, dt_iop_module_t *self)
1139 1140
{
  // capture gui color picked event.
1141
  if(darktable.gui->reset) return FALSE;
1142
  if(self->picked_color_max[0] < self->picked_color_min[0]) return FALSE;
1143
  if(self->request_color_pick == DT_REQUEST_COLORPICK_OFF) return FALSE;
1144
  static float old[4] = { 0.0f, 0.0f, 0.0f, 0.0f };
1145
  const float *grayrgb = self->picked_color;