1 /*
2 This file is part of darktable,
3 Copyright (C) 2010-2021 darktable developers.
4
5 darktable is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
9
10 darktable is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with darktable. If not, see <http://www.gnu.org/licenses/>.
17 */
18 #ifdef HAVE_CONFIG_H
19 #include "config.h"
20 #endif
21 #include "bauhaus/bauhaus.h"
22 #include "common/colorspaces_inline_conversions.h"
23 #include "common/darktable.h"
24 #include "common/math.h"
25 #include "common/opencl.h"
26 #include "control/control.h"
27 #include "develop/develop.h"
28 #include "develop/imageop_math.h"
29 #include "develop/imageop_gui.h"
30 #include "dtgtk/button.h"
31 #include "gui/accelerators.h"
32 #include "gui/gtk.h"
33 #include "gui/presets.h"
34 #include "gui/color_picker_proxy.h"
35 #include "iop/iop_api.h"
36 #include <assert.h>
37 #include <math.h>
38 #include <stdlib.h>
39 #include <string.h>
40
41 DT_MODULE_INTROSPECTION(2, dt_iop_profilegamma_params_t)
42
43 typedef enum dt_iop_profilegamma_mode_t
44 {
45 PROFILEGAMMA_LOG = 0, // $DESCRIPTION: "logarithmic"
46 PROFILEGAMMA_GAMMA = 1 // $DESCRIPTION: "gamma"
47 } dt_iop_profilegamma_mode_t;
48
49 typedef struct dt_iop_profilegamma_params_t
50 {
51 dt_iop_profilegamma_mode_t mode; // $DEFAULT: PROFILEGAMMA_LOG
52 float linear; // $MIN: 0.0 $MAX: 1.0 $DEFAULT: 0.1
53 float gamma; // $MIN: 0.0 $MAX: 1.0 $DEFAULT: 0.45
54 float dynamic_range; // $MIN: 0.01 $MAX: 32.0 $DEFAULT: 10.0 $DESCRIPTION: "dynamic range"
55 float grey_point; // $MIN: 0.1 $MAX: 100.0 $DEFAULT: 18.0 $DESCRIPTION: "middle gray luma"
56 float shadows_range; // $MIN: -16.0 $MAX: 16.0 $DEFAULT: -5.0 $DESCRIPTION: "black relative exposure"
57 float security_factor; // $MIN: -100.0 $MAX: 100.0 $DEFAULT: 0.0 $DESCRIPTION: "safety factor"
58 } dt_iop_profilegamma_params_t;
59
60 typedef struct dt_iop_profilegamma_gui_data_t
61 {
62 GtkWidget *mode;
63 GtkWidget *mode_stack;
64 GtkWidget *linear;
65 GtkWidget *gamma;
66 GtkWidget *dynamic_range;
67 GtkWidget *grey_point;
68 GtkWidget *shadows_range;
69 GtkWidget *security_factor;
70 GtkWidget *auto_button;
71 } dt_iop_profilegamma_gui_data_t;
72
73 typedef struct dt_iop_profilegamma_data_t
74 {
75 dt_iop_profilegamma_mode_t mode;
76 float linear;
77 float gamma;
78 float table[0x10000]; // precomputed look-up table
79 float unbounded_coeffs[3]; // approximation for extrapolation of curve
80 float dynamic_range;
81 float grey_point;
82 float shadows_range;
83 float security_factor;
84 } dt_iop_profilegamma_data_t;
85
86 typedef struct dt_iop_profilegamma_global_data_t
87 {
88 int kernel_profilegamma;
89 int kernel_profilegamma_log;
90 } dt_iop_profilegamma_global_data_t;
91
92
name()93 const char *name()
94 {
95 return _("unbreak input profile");
96 }
97
description(struct dt_iop_module_t * self)98 const char *description(struct dt_iop_module_t *self)
99 {
100 return dt_iop_set_description(self, _("correct input color profiles meant to be applied on non-linear RGB"),
101 _("corrective"),
102 _("linear, RGB, display-referred"),
103 _("non-linear, RGB"),
104 _("non-linear, RGB, display-referred"));
105 }
106
default_group()107 int default_group()
108 {
109 return IOP_GROUP_COLOR | IOP_GROUP_TECHNICAL;
110 }
111
flags()112 int flags()
113 {
114 return IOP_FLAGS_ONE_INSTANCE | IOP_FLAGS_ALLOW_TILING | IOP_FLAGS_INCLUDE_IN_STYLES
115 | IOP_FLAGS_SUPPORTS_BLENDING;
116 }
117
default_colorspace(dt_iop_module_t * self,dt_dev_pixelpipe_t * pipe,dt_dev_pixelpipe_iop_t * piece)118 int default_colorspace(dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
119 {
120 return iop_cs_rgb;
121 }
122
init_presets(dt_iop_module_so_t * self)123 void init_presets(dt_iop_module_so_t *self)
124 {
125 dt_iop_profilegamma_params_t p;
126 memset(&p, 0, sizeof(p));
127
128 p.mode = PROFILEGAMMA_LOG;
129 p.grey_point = 18.00f;
130 p.security_factor = 0.0f;
131
132 // 16 EV preset
133 p.dynamic_range = 16.0f;
134 p.shadows_range = -12.0f;
135 dt_gui_presets_add_generic(_("16 EV dynamic range (generic)"), self->op,
136 self->version(), &p, sizeof(p), 1, DEVELOP_BLEND_CS_RGB_DISPLAY);
137
138 // 14 EV preset
139 p.dynamic_range = 14.0f;
140 p.shadows_range = -10.50f;
141 dt_gui_presets_add_generic(_("14 EV dynamic range (generic)"), self->op,
142 self->version(), &p, sizeof(p), 1, DEVELOP_BLEND_CS_RGB_DISPLAY);
143
144 // 12 EV preset
145 p.dynamic_range = 12.0f;
146 p.shadows_range = -9.0f;
147 dt_gui_presets_add_generic(_("12 EV dynamic range (generic)"), self->op,
148 self->version(), &p, sizeof(p), 1, DEVELOP_BLEND_CS_RGB_DISPLAY);
149
150 // 10 EV preset
151 p.dynamic_range = 10.0f;
152 p.shadows_range = -7.50f;
153 dt_gui_presets_add_generic(_("10 EV dynamic range (generic)"), self->op,
154 self->version(), &p, sizeof(p), 1, DEVELOP_BLEND_CS_RGB_DISPLAY);
155
156 // 08 EV preset
157 p.dynamic_range = 8.0f;
158 p.shadows_range = -6.0f;
159 dt_gui_presets_add_generic(_("08 EV dynamic range (generic)"), self->op,
160 self->version(), &p, sizeof(p), 1, DEVELOP_BLEND_CS_RGB_DISPLAY);
161 }
162
163
legacy_params(dt_iop_module_t * self,const void * const old_params,const int old_version,void * new_params,const int new_version)164 int legacy_params(dt_iop_module_t *self, const void *const old_params, const int old_version, void *new_params,
165 const int new_version)
166 {
167 if(old_version == 1 && new_version == 2)
168 {
169 typedef struct dt_iop_profilegamma_params_v1_t
170 {
171 float linear;
172 float gamma;
173 } dt_iop_profilegamma_params_v1_t;
174
175 dt_iop_profilegamma_params_v1_t *o = (dt_iop_profilegamma_params_v1_t *)old_params;
176 dt_iop_profilegamma_params_t *n = (dt_iop_profilegamma_params_t *)new_params;
177 dt_iop_profilegamma_params_t *d = (dt_iop_profilegamma_params_t *)self->default_params;
178
179 *n = *d; // start with a fresh copy of default parameters
180
181 n->linear = o->linear;
182 n->gamma = o->gamma;
183 n->mode = PROFILEGAMMA_GAMMA;
184 return 0;
185 }
186 return 1;
187 }
188
189
190 #ifdef HAVE_OPENCL
process_cl(struct dt_iop_module_t * self,dt_dev_pixelpipe_iop_t * piece,cl_mem dev_in,cl_mem dev_out,const dt_iop_roi_t * const roi_in,const dt_iop_roi_t * const roi_out)191 int process_cl(struct dt_iop_module_t *self, dt_dev_pixelpipe_iop_t *piece, cl_mem dev_in, cl_mem dev_out,
192 const dt_iop_roi_t *const roi_in, const dt_iop_roi_t *const roi_out)
193 {
194 dt_iop_profilegamma_data_t *d = (dt_iop_profilegamma_data_t *)piece->data;
195 dt_iop_profilegamma_global_data_t *gd = (dt_iop_profilegamma_global_data_t *)self->global_data;
196
197 cl_int err = -999;
198 const int devid = piece->pipe->devid;
199 const int width = roi_in->width;
200 const int height = roi_in->height;
201 cl_mem dev_table = NULL;
202 cl_mem dev_coeffs = NULL;
203
204
205 size_t sizes[3] = { ROUNDUPWD(width), ROUNDUPHT(height), 1 };
206
207 if(d->mode == PROFILEGAMMA_LOG)
208 {
209 const float dynamic_range = d->dynamic_range;
210 const float shadows_range = d->shadows_range;
211 const float grey = d->grey_point / 100.0f;
212 dt_opencl_set_kernel_arg(devid, gd->kernel_profilegamma_log, 0, sizeof(cl_mem), (void *)&dev_in);
213 dt_opencl_set_kernel_arg(devid, gd->kernel_profilegamma_log, 1, sizeof(cl_mem), (void *)&dev_out);
214 dt_opencl_set_kernel_arg(devid, gd->kernel_profilegamma_log, 2, sizeof(int), (void *)&width);
215 dt_opencl_set_kernel_arg(devid, gd->kernel_profilegamma_log, 3, sizeof(int), (void *)&height);
216 dt_opencl_set_kernel_arg(devid, gd->kernel_profilegamma_log, 4, sizeof(float), (void *)&dynamic_range);
217 dt_opencl_set_kernel_arg(devid, gd->kernel_profilegamma_log, 5, sizeof(float), (void *)&shadows_range);
218 dt_opencl_set_kernel_arg(devid, gd->kernel_profilegamma_log, 6, sizeof(float), (void *)&grey);
219
220 err = dt_opencl_enqueue_kernel_2d(devid, gd->kernel_profilegamma_log, sizes);
221 if(err != CL_SUCCESS) goto error;
222 return TRUE;
223 }
224 else if(d->mode == PROFILEGAMMA_GAMMA)
225 {
226 dev_table = dt_opencl_copy_host_to_device(devid, d->table, 256, 256, sizeof(float));
227 if(dev_table == NULL) goto error;
228
229 dev_coeffs = dt_opencl_copy_host_to_device_constant(devid, sizeof(float) * 3, d->unbounded_coeffs);
230 if(dev_coeffs == NULL) goto error;
231
232 dt_opencl_set_kernel_arg(devid, gd->kernel_profilegamma, 0, sizeof(cl_mem), (void *)&dev_in);
233 dt_opencl_set_kernel_arg(devid, gd->kernel_profilegamma, 1, sizeof(cl_mem), (void *)&dev_out);
234 dt_opencl_set_kernel_arg(devid, gd->kernel_profilegamma, 2, sizeof(int), (void *)&width);
235 dt_opencl_set_kernel_arg(devid, gd->kernel_profilegamma, 3, sizeof(int), (void *)&height);
236 dt_opencl_set_kernel_arg(devid, gd->kernel_profilegamma, 4, sizeof(cl_mem), (void *)&dev_table);
237 dt_opencl_set_kernel_arg(devid, gd->kernel_profilegamma, 5, sizeof(cl_mem), (void *)&dev_coeffs);
238
239 err = dt_opencl_enqueue_kernel_2d(devid, gd->kernel_profilegamma, sizes);
240 if(err != CL_SUCCESS)
241 {
242 dt_opencl_release_mem_object(dev_table);
243 dt_opencl_release_mem_object(dev_coeffs);
244 goto error;
245 }
246
247 dt_opencl_release_mem_object(dev_table);
248 dt_opencl_release_mem_object(dev_coeffs);
249 return TRUE;
250 }
251
252 error:
253 dt_print(DT_DEBUG_OPENCL, "[opencl_profilegamma] couldn't enqueue kernel! %d\n", err);
254 return FALSE;
255 }
256 #endif
257
process(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)258 void process(dt_iop_module_t *self, dt_dev_pixelpipe_iop_t *piece, const void *const ivoid, void *const ovoid,
259 const dt_iop_roi_t *const roi_in, const dt_iop_roi_t *const roi_out)
260 {
261 dt_iop_profilegamma_data_t *data = (dt_iop_profilegamma_data_t *)piece->data;
262
263 const int ch = piece->colors;
264
265 switch(data->mode)
266 {
267 case PROFILEGAMMA_LOG:
268 {
269 const float grey = data->grey_point / 100.0f;
270
271 /** The log2(x) -> -INF when x -> 0
272 * thus very low values (noise) will get even lower, resulting in noise negative amplification,
273 * which leads to pepper noise in shadows. To avoid that, we need to clip values that are noise for sure.
274 * Using 16 bits RAW data, the black value (known by rawspeed for every manufacturer) could be used as a threshold.
275 * However, at this point of the pixelpipe, the RAW levels have already been corrected and everything can happen with black levels
276 * in the exposure module. So we define the threshold as the first non-null 16 bit integer
277 */
278 const float noise = powf(2.0f, -16.0f);
279
280 #ifdef _OPENMP
281 #pragma omp parallel for SIMD() default(none) \
282 dt_omp_firstprivate(ch, grey, ivoid, ovoid, roi_out, noise) \
283 shared(data) \
284 schedule(static)
285 #endif
286 for(size_t k = 0; k < (size_t)ch * roi_out->width * roi_out->height; k++)
287 {
288 float tmp = ((const float *)ivoid)[k] / grey;
289 if (tmp < noise) tmp = noise;
290 tmp = (fastlog2(tmp) - data->shadows_range) / (data->dynamic_range);
291
292 if (tmp < noise)
293 {
294 ((float *)ovoid)[k] = noise;
295 }
296 else
297 {
298 ((float *)ovoid)[k] = tmp;
299 }
300 }
301 break;
302 }
303
304 case PROFILEGAMMA_GAMMA:
305 {
306 #ifdef _OPENMP
307 #pragma omp parallel for default(none) \
308 dt_omp_firstprivate(ch, ivoid, ovoid, roi_out) \
309 shared(data) \
310 schedule(static)
311 #endif
312 for(int k = 0; k < roi_out->height; k++)
313 {
314 const float *in = ((float *)ivoid) + (size_t)ch * k * roi_out->width;
315 float *out = ((float *)ovoid) + (size_t)ch * k * roi_out->width;
316
317 for(int j = 0; j < roi_out->width; j++, in += ch, out += ch)
318 {
319 for(int i = 0; i < 3; i++)
320 {
321 // use base curve for values < 1, else use extrapolation.
322 if(in[i] < 1.0f)
323 out[i] = data->table[CLAMP((int)(in[i] * 0x10000ul), 0, 0xffff)];
324 else
325 out[i] = dt_iop_eval_exp(data->unbounded_coeffs, in[i]);
326 }
327 }
328 }
329 break;
330 }
331 }
332
333 if(piece->pipe->mask_display & DT_DEV_PIXELPIPE_DISPLAY_MASK)
334 dt_iop_alpha_copy(ivoid, ovoid, roi_out->width, roi_out->height);
335 }
336
apply_auto_grey(dt_iop_module_t * self)337 static void apply_auto_grey(dt_iop_module_t *self)
338 {
339 if(darktable.gui->reset) return;
340 dt_iop_profilegamma_params_t *p = (dt_iop_profilegamma_params_t *)self->params;
341 dt_iop_profilegamma_gui_data_t *g = (dt_iop_profilegamma_gui_data_t *)self->gui_data;
342
343 float grey = fmax(fmax(self->picked_color[0], self->picked_color[1]), self->picked_color[2]);
344 p->grey_point = 100.f * grey;
345
346 ++darktable.gui->reset;
347 dt_bauhaus_slider_set(g->grey_point, p->grey_point);
348 --darktable.gui->reset;
349
350 dt_dev_add_history_item(darktable.develop, self, TRUE);
351 }
352
apply_auto_black(dt_iop_module_t * self)353 static void apply_auto_black(dt_iop_module_t *self)
354 {
355 if(darktable.gui->reset) return;
356 dt_iop_profilegamma_params_t *p = (dt_iop_profilegamma_params_t *)self->params;
357 dt_iop_profilegamma_gui_data_t *g = (dt_iop_profilegamma_gui_data_t *)self->gui_data;
358
359 float noise = powf(2.0f, -16.0f);
360
361 // Black
362 float black = fmax(fmax(self->picked_color_min[0], self->picked_color_min[1]), self->picked_color_min[2]);
363 float EVmin = Log2Thres(black / (p->grey_point / 100.0f), noise);
364 EVmin *= (1.0f + p->security_factor / 100.0f);
365
366 p->shadows_range = EVmin;
367
368 ++darktable.gui->reset;
369 dt_bauhaus_slider_set(g->shadows_range, p->shadows_range);
370 --darktable.gui->reset;
371
372 dt_dev_add_history_item(darktable.develop, self, TRUE);
373 }
374
apply_auto_dynamic_range(dt_iop_module_t * self)375 static void apply_auto_dynamic_range(dt_iop_module_t *self)
376 {
377 if(darktable.gui->reset) return;
378 dt_iop_profilegamma_params_t *p = (dt_iop_profilegamma_params_t *)self->params;
379 dt_iop_profilegamma_gui_data_t *g = (dt_iop_profilegamma_gui_data_t *)self->gui_data;
380
381 float noise = powf(2.0f, -16.0f);
382
383 // Black
384 float EVmin = p->shadows_range;
385
386 // White
387 float white = fmax(fmax(self->picked_color_max[0], self->picked_color_max[1]), self->picked_color_max[2]);
388 float EVmax = Log2Thres(white / (p->grey_point / 100.0f), noise);
389 EVmax *= (1.0f + p->security_factor / 100.0f);
390
391 p->dynamic_range = EVmax - EVmin;
392
393 ++darktable.gui->reset;
394 dt_bauhaus_slider_set(g->dynamic_range, p->dynamic_range);
395 --darktable.gui->reset;
396
397 dt_dev_add_history_item(darktable.develop, self, TRUE);
398 }
399
apply_autotune(dt_iop_module_t * self)400 static void apply_autotune(dt_iop_module_t *self)
401 {
402 dt_iop_profilegamma_params_t *p = (dt_iop_profilegamma_params_t *)self->params;
403 dt_iop_profilegamma_gui_data_t *g = (dt_iop_profilegamma_gui_data_t *)self->gui_data;
404
405 float noise = powf(2.0f, -16.0f);
406
407 // Grey
408 float grey = fmax(fmax(self->picked_color[0], self->picked_color[1]), self->picked_color[2]);
409 p->grey_point = 100.f * grey;
410
411 // Black
412 float black = fmax(fmax(self->picked_color_min[0], self->picked_color_min[1]), self->picked_color_min[2]);
413 float EVmin = Log2Thres(black / (p->grey_point / 100.0f), noise);
414 EVmin *= (1.0f + p->security_factor / 100.0f);
415
416 // White
417 float white = fmax(fmax(self->picked_color_max[0], self->picked_color_max[1]), self->picked_color_max[2]);
418 float EVmax = Log2Thres(white / (p->grey_point / 100.0f), noise);
419 EVmax *= (1.0f + p->security_factor / 100.0f);
420
421 p->shadows_range = EVmin;
422 p->dynamic_range = EVmax - EVmin;
423
424 ++darktable.gui->reset;
425 dt_bauhaus_slider_set(g->grey_point, p->grey_point);
426 dt_bauhaus_slider_set(g->shadows_range, p->shadows_range);
427 dt_bauhaus_slider_set(g->dynamic_range, p->dynamic_range);
428 --darktable.gui->reset;
429
430 dt_dev_add_history_item(darktable.develop, self, TRUE);
431 }
432
433
gui_changed(dt_iop_module_t * self,GtkWidget * w,void * previous)434 void gui_changed(dt_iop_module_t *self, GtkWidget *w, void *previous)
435 {
436 dt_iop_profilegamma_gui_data_t *g = (dt_iop_profilegamma_gui_data_t *)self->gui_data;
437 dt_iop_profilegamma_params_t *p = (dt_iop_profilegamma_params_t *)self->params;
438
439 if(w == g->mode)
440 {
441 if(p->mode == PROFILEGAMMA_LOG)
442 {
443 gtk_stack_set_visible_child_name(GTK_STACK(g->mode_stack), "log");
444 }
445 else
446 {
447 gtk_stack_set_visible_child_name(GTK_STACK(g->mode_stack), "gamma");
448 }
449 }
450 else if(w == g->security_factor)
451 {
452 float prev = *(float *)previous;
453 float ratio = (p->security_factor - prev) / (prev + 100.0f);
454
455 float EVmin = p->shadows_range;
456 EVmin = EVmin + ratio * EVmin;
457
458 float EVmax = p->dynamic_range + p->shadows_range;
459 EVmax = EVmax + ratio * EVmax;
460
461 p->dynamic_range = EVmax - EVmin;
462 p->shadows_range = EVmin;
463
464 ++darktable.gui->reset;
465 dt_bauhaus_slider_set_soft(g->dynamic_range, p->dynamic_range);
466 dt_bauhaus_slider_set_soft(g->shadows_range, p->shadows_range);
467 --darktable.gui->reset;
468 }
469 }
470
color_picker_apply(dt_iop_module_t * self,GtkWidget * picker,dt_dev_pixelpipe_iop_t * piece)471 void color_picker_apply(dt_iop_module_t *self, GtkWidget *picker, dt_dev_pixelpipe_iop_t *piece)
472 {
473 dt_iop_profilegamma_gui_data_t *g = (dt_iop_profilegamma_gui_data_t *)self->gui_data;
474 if (picker == g->grey_point)
475 apply_auto_grey(self);
476 else if(picker == g->shadows_range)
477 apply_auto_black(self);
478 else if(picker == g->dynamic_range)
479 apply_auto_dynamic_range(self);
480 else if(picker == g->auto_button)
481 apply_autotune(self);
482 else
483 fprintf(stderr, "[profile_gamma] unknown color picker\n");
484 }
485
commit_params(dt_iop_module_t * self,dt_iop_params_t * p1,dt_dev_pixelpipe_t * pipe,dt_dev_pixelpipe_iop_t * piece)486 void commit_params(dt_iop_module_t *self, dt_iop_params_t *p1, dt_dev_pixelpipe_t *pipe,
487 dt_dev_pixelpipe_iop_t *piece)
488 {
489 dt_iop_profilegamma_params_t *p = (dt_iop_profilegamma_params_t *)p1;
490 dt_iop_profilegamma_data_t *d = (dt_iop_profilegamma_data_t *)piece->data;
491
492 const float linear = p->linear;
493 const float gamma = p->gamma;
494
495 d->linear = p->linear;
496 d->gamma = p->gamma;
497
498 float a, b, c, g;
499 if(gamma == 1.0)
500 {
501 #ifdef _OPENMP
502 #pragma omp parallel for default(none) shared(d) schedule(static)
503 #endif
504 for(int k = 0; k < 0x10000; k++) d->table[k] = 1.0 * k / 0x10000;
505 }
506 else
507 {
508 if(linear == 0.0)
509 {
510 #ifdef _OPENMP
511 #pragma omp parallel for default(none) \
512 dt_omp_firstprivate(gamma) \
513 shared(d) \
514 schedule(static)
515 #endif
516 for(int k = 0; k < 0x10000; k++) d->table[k] = powf(1.00 * k / 0x10000, gamma);
517 }
518 else
519 {
520 if(linear < 1.0)
521 {
522 g = gamma * (1.0 - linear) / (1.0 - gamma * linear);
523 a = 1.0 / (1.0 + linear * (g - 1));
524 b = linear * (g - 1) * a;
525 c = powf(a * linear + b, g) / linear;
526 }
527 else
528 {
529 a = b = g = 0.0;
530 c = 1.0;
531 }
532 #ifdef _OPENMP
533 #pragma omp parallel for default(none) \
534 dt_omp_firstprivate(linear) \
535 shared(d, a, b, c, g) \
536 schedule(static)
537 #endif
538 for(int k = 0; k < 0x10000; k++)
539 {
540 float tmp;
541 if(k < 0x10000 * linear)
542 tmp = c * k / 0x10000;
543 else
544 tmp = powf(a * k / 0x10000 + b, g);
545 d->table[k] = tmp;
546 }
547 }
548 }
549
550 // now the extrapolation stuff:
551 const float x[4] = { 0.7f, 0.8f, 0.9f, 1.0f };
552 const float y[4]
553 = { d->table[CLAMP((int)(x[0] * 0x10000ul), 0, 0xffff)],
554 d->table[CLAMP((int)(x[1] * 0x10000ul), 0, 0xffff)],
555 d->table[CLAMP((int)(x[2] * 0x10000ul), 0, 0xffff)],
556 d->table[CLAMP((int)(x[3] * 0x10000ul), 0, 0xffff)] };
557 dt_iop_estimate_exp(x, y, 4, d->unbounded_coeffs);
558
559 d->dynamic_range = p->dynamic_range;
560 d->grey_point = p->grey_point;
561 d->shadows_range = p->shadows_range;
562 d->security_factor = p->security_factor;
563 d->mode = p->mode;
564
565 //piece->process_cl_ready = 1;
566
567 // no OpenCL for log yet.
568 //if(d->mode == PROFILEGAMMA_LOG) piece->process_cl_ready = 0;
569 }
570
init_pipe(dt_iop_module_t * self,dt_dev_pixelpipe_t * pipe,dt_dev_pixelpipe_iop_t * piece)571 void init_pipe(dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
572 {
573 piece->data = calloc(1, sizeof(dt_iop_profilegamma_data_t));
574 }
575
cleanup_pipe(dt_iop_module_t * self,dt_dev_pixelpipe_t * pipe,dt_dev_pixelpipe_iop_t * piece)576 void cleanup_pipe(dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
577 {
578 free(piece->data);
579 piece->data = NULL;
580 }
581
gui_reset(dt_iop_module_t * self)582 void gui_reset(dt_iop_module_t *self)
583 {
584 dt_iop_color_picker_reset(self, TRUE);
585 }
586
gui_update(dt_iop_module_t * self)587 void gui_update(dt_iop_module_t *self)
588 {
589 dt_iop_profilegamma_gui_data_t *g = (dt_iop_profilegamma_gui_data_t *)self->gui_data;
590 dt_iop_profilegamma_params_t *p = (dt_iop_profilegamma_params_t *)self->params;
591
592 dt_iop_color_picker_reset(self, TRUE);
593
594 dt_bauhaus_combobox_set(g->mode, p->mode);
595 dt_bauhaus_slider_set_soft(g->linear, p->linear);
596 dt_bauhaus_slider_set_soft(g->gamma, p->gamma);
597 dt_bauhaus_slider_set_soft(g->dynamic_range, p->dynamic_range);
598 dt_bauhaus_slider_set_soft(g->grey_point, p->grey_point);
599 dt_bauhaus_slider_set_soft(g->shadows_range, p->shadows_range);
600 dt_bauhaus_slider_set_soft(g->security_factor, p->security_factor);
601
602 gui_changed(self, g->mode, 0);
603 }
604
init_global(dt_iop_module_so_t * module)605 void init_global(dt_iop_module_so_t *module)
606 {
607 const int program = 2; // basic.cl, from programs.conf
608 dt_iop_profilegamma_global_data_t *gd
609 = (dt_iop_profilegamma_global_data_t *)malloc(sizeof(dt_iop_profilegamma_global_data_t));
610
611 module->data = gd;
612 gd->kernel_profilegamma = dt_opencl_create_kernel(program, "profilegamma");
613 gd->kernel_profilegamma_log = dt_opencl_create_kernel(program, "profilegamma_log");
614 }
615
cleanup_global(dt_iop_module_so_t * module)616 void cleanup_global(dt_iop_module_so_t *module)
617 {
618 dt_iop_profilegamma_global_data_t *gd = (dt_iop_profilegamma_global_data_t *)module->data;
619 dt_opencl_free_kernel(gd->kernel_profilegamma);
620 dt_opencl_free_kernel(gd->kernel_profilegamma_log);
621 free(module->data);
622 module->data = NULL;
623 }
624
625
gui_init(dt_iop_module_t * self)626 void gui_init(dt_iop_module_t *self)
627 {
628 dt_iop_profilegamma_gui_data_t *g = IOP_GUI_ALLOC(profilegamma);
629
630 // prepare the modes widgets stack
631 g->mode_stack = gtk_stack_new();
632 gtk_stack_set_homogeneous(GTK_STACK(g->mode_stack), FALSE);
633
634 /**** GAMMA MODE ***/
635
636 GtkWidget *vbox_gamma = self->widget = GTK_WIDGET(gtk_box_new(GTK_ORIENTATION_VERTICAL, DT_BAUHAUS_SPACE));
637
638 g->linear = dt_bauhaus_slider_from_params(self, N_("linear"));
639 dt_bauhaus_slider_set_digits(g->linear, 4);
640 gtk_widget_set_tooltip_text(g->linear, _("linear part"));
641
642 g->gamma = dt_bauhaus_slider_from_params(self, N_("gamma"));
643 dt_bauhaus_slider_set_digits(g->gamma, 4);
644 gtk_widget_set_tooltip_text(g->gamma, _("gamma exponential factor"));
645
646 gtk_stack_add_named(GTK_STACK(g->mode_stack), vbox_gamma, "gamma");
647
648 /**** LOG MODE ****/
649
650 GtkWidget *vbox_log = self->widget = GTK_WIDGET(gtk_box_new(GTK_ORIENTATION_VERTICAL, DT_BAUHAUS_SPACE));
651
652 g->grey_point
653 = dt_color_picker_new(self, DT_COLOR_PICKER_AREA, dt_bauhaus_slider_from_params(self, "grey_point"));
654 dt_bauhaus_slider_set_step(g->grey_point, 0.5);
655 dt_bauhaus_slider_set_format(g->grey_point, "%.2f %%");
656 gtk_widget_set_tooltip_text(g->grey_point, _("adjust to match the average luma of the subject"));
657
658 g->shadows_range
659 = dt_color_picker_new(self, DT_COLOR_PICKER_AREA, dt_bauhaus_slider_from_params(self, "shadows_range"));
660 dt_bauhaus_slider_set_soft_max(g->shadows_range, 0.0);
661 dt_bauhaus_slider_set_format(g->shadows_range, "%.2f EV");
662 gtk_widget_set_tooltip_text(g->shadows_range, _("number of stops between middle gray and pure black\nthis is a reading a posemeter would give you on the scene"));
663
664 g->dynamic_range
665 = dt_color_picker_new(self, DT_COLOR_PICKER_AREA, dt_bauhaus_slider_from_params(self, "dynamic_range"));
666 dt_bauhaus_slider_set_soft_range(g->dynamic_range, 0.5, 16.0);
667 dt_bauhaus_slider_set_format(g->dynamic_range, "%.2f EV");
668 gtk_widget_set_tooltip_text(g->dynamic_range, _("number of stops between pure black and pure white\nthis is a reading a posemeter would give you on the scene"));
669
670 gtk_box_pack_start(GTK_BOX(vbox_log), dt_ui_section_label_new(_("optimize automatically")), FALSE, FALSE, 0);
671
672 g->security_factor = dt_bauhaus_slider_from_params(self, "security_factor");
673 dt_bauhaus_slider_set_step(g->security_factor, 0.1);
674 dt_bauhaus_slider_set_format(g->security_factor, "%.2f %%");
675 gtk_widget_set_tooltip_text(g->security_factor, _("enlarge or shrink the computed dynamic range\nthis is useful when noise perturbates the measurements"));
676
677 g->auto_button = dt_color_picker_new(self, DT_COLOR_PICKER_AREA, dt_bauhaus_combobox_new(self));
678 dt_bauhaus_widget_set_label(g->auto_button, NULL, N_("auto tune levels"));
679 gtk_widget_set_tooltip_text(g->auto_button, _("make an optimization with some guessing"));
680 gtk_box_pack_start(GTK_BOX(vbox_log), g->auto_button, TRUE, TRUE, 0);
681
682 gtk_stack_add_named(GTK_STACK(g->mode_stack), vbox_log, "log");
683
684 // start building top level widget
685 self->widget = gtk_box_new(GTK_ORIENTATION_VERTICAL, DT_BAUHAUS_SPACE);
686
687 g->mode = dt_bauhaus_combobox_from_params(self, N_("mode"));
688 gtk_widget_set_tooltip_text(g->mode, _("tone mapping method"));
689
690 gtk_box_pack_start(GTK_BOX(self->widget), g->mode_stack, TRUE, TRUE, 0);
691 }
692
693
694 // modelines: These editor modelines have been set for all relevant files by tools/update_modelines.sh
695 // vim: shiftwidth=2 expandtab tabstop=2 cindent
696 // kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
697