Ansel 0.0
A darktable fork - bloat + design vision
Loading...
Searching...
No Matches
colorout.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 Bruce Guenter.
6 Copyright (C) 2010-2011 Henrik Andersson.
7 Copyright (C) 2010 Milan Knížek.
8 Copyright (C) 2010, 2012-2014 Pascal de Bruijn.
9 Copyright (C) 2010 Stuart Henderson.
10 Copyright (C) 2011 Antony Dovgal.
11 Copyright (C) 2011 Robert Bieber.
12 Copyright (C) 2011 Rostyslav Pidgornyi.
13 Copyright (C) 2011-2017, 2019 Tobias Ellinghaus.
14 Copyright (C) 2011-2014, 2016-2017 Ulrich Pegelow.
15 Copyright (C) 2012, 2020 Aldric Renaudin.
16 Copyright (C) 2012 Christian Tellefsen.
17 Copyright (C) 2012 John Sheu.
18 Copyright (C) 2012 Jérémy Rosen.
19 Copyright (C) 2012 Michal Babej.
20 Copyright (C) 2012 Richard Wonka.
21 Copyright (C) 2013-2016 Roman Lebedev.
22 Copyright (C) 2013 Thomas Pryds.
23 Copyright (C) 2014 Edouard Gomez.
24 Copyright (C) 2015 Pedro Côrte-Real.
25 Copyright (C) 2017 Heiko Bauke.
26 Copyright (C) 2018, 2020, 2022-2026 Aurélien PIERRE.
27 Copyright (C) 2018-2019 Edgardo Hoszowski.
28 Copyright (C) 2018 Maurizio Paglia.
29 Copyright (C) 2018, 2020-2022 Pascal Obry.
30 Copyright (C) 2018 rawfiner.
31 Copyright (C) 2019 Andreas Schneider.
32 Copyright (C) 2019 jakubfi.
33 Copyright (C) 2020 Chris Elston.
34 Copyright (C) 2020 Diederik Ter Rahe.
35 Copyright (C) 2020 Hubert Kowalski.
36 Copyright (C) 2020-2021 Ralf Brown.
37 Copyright (C) 2021 Sakari Kapanen.
38 Copyright (C) 2022 Hanno Schwalm.
39 Copyright (C) 2022 Martin Bařinka.
40 Copyright (C) 2022 Philipp Lutz.
41 Copyright (C) 2023 Luca Zulberti.
42
43 darktable is free software: you can redistribute it and/or modify
44 it under the terms of the GNU General Public License as published by
45 the Free Software Foundation, either version 3 of the License, or
46 (at your option) any later version.
47
48 darktable is distributed in the hope that it will be useful,
49 but WITHOUT ANY WARRANTY; without even the implied warranty of
50 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
51 GNU General Public License for more details.
52
53 You should have received a copy of the GNU General Public License
54 along with darktable. If not, see <http://www.gnu.org/licenses/>.
55*/
56#ifdef HAVE_CONFIG_H
57#include "common/darktable.h"
58#include "config.h"
59#endif
60#include "bauhaus/bauhaus.h"
61#include "common/colorspaces.h"
63#include "common/matrices.h"
65#include "common/imagebuf.h"
66#include "common/iop_profile.h"
67#include "common/opencl.h"
68#include "control/conf.h"
69#include "control/control.h"
70#include "develop/develop.h"
71#include "develop/imageop.h"
73
74#include "gui/gtk.h"
75#include "iop/iop_api.h"
76
77#include <assert.h>
78#include <gdk/gdkkeysyms.h>
79#include <math.h>
80#include <stdlib.h>
81#include <string.h>
82
83// max iccprofile file name length
84// must be in synch with dt_colorspaces_color_profile_t
85#define DT_IOP_COLOR_ICC_LEN 512
86#define LUT_SAMPLES 0x10000
87
89
99
104
111
112
113
114const char *name()
115{
116 return _("output color profile");
117}
118
119
120const char **description(struct dt_iop_module_t *self)
121{
122 return dt_iop_set_description(self, _("convert pipeline reference RGB to any display RGB\n"
123 "using color profiles to remap RGB values"),
124 _("mandatory"),
125 _("linear or non-linear, RGB or Lab, display-referred"),
126 _("defined by profile"),
127 _("non-linear, RGB or Lab, display-referred"));
128}
129
130
132{
133 return IOP_GROUP_TECHNICAL;
134}
135
140
142{
143 return IOP_CS_RGB;
144}
145
147 const dt_dev_pixelpipe_t *pipe)
148{
151
152 /* Export overrides are applied in commit_params(), but the sealed pipeline needs the buffer
153 * contract before that. Mirror the only colorspace-affecting override here so colorout
154 * advertises the current RGB/Lab contract instead of the previous image runtime state. */
156 type = pipe->icc_type;
157
159}
160
167
170{
171 dsc->channels = 4;
172 dsc->datatype = TYPE_FLOAT;
173 dsc->cst = _colorout_input_format_cst(self, pipe);
174}
175
178{
179 dsc->channels = 4;
180 dsc->datatype = TYPE_FLOAT;
181 dsc->cst = _colorout_output_format_cst(self, pipe);
182}
183
184int legacy_params(dt_iop_module_t *self, const void *const old_params, const int old_version,
185 void *new_params, const int new_version)
186{
187#define DT_IOP_COLOR_ICC_LEN_V4 100
188 /* if(old_version == 1 && new_version == 2)
189 {
190 dt_iop_colorout_params_t *o = (dt_iop_colorout_params_t *)old_params;
191 dt_iop_colorout_params_t *n = (dt_iop_colorout_params_t *)new_params;
192 memcpy(n,o,sizeof(dt_iop_colorout_params_t));
193 n->seq = 0;
194 return 0;
195 }*/
196 if((old_version == 2 || old_version == 3) && new_version == 5)
197 {
198 typedef struct dt_iop_colorout_params_v3_t
199 {
200 char iccprofile[DT_IOP_COLOR_ICC_LEN_V4];
201 char displayprofile[DT_IOP_COLOR_ICC_LEN_V4];
203 dt_iop_color_intent_t displayintent;
204 char softproof_enabled;
205 char softproofprofile[DT_IOP_COLOR_ICC_LEN_V4];
206 dt_iop_color_intent_t softproofintent;
207 } dt_iop_colorout_params_v3_t;
208
209
210 dt_iop_colorout_params_v3_t *o = (dt_iop_colorout_params_v3_t *)old_params;
212 memset(n, 0, sizeof(dt_iop_colorout_params_t));
213
214 if(!strcmp(o->iccprofile, "sRGB"))
215 n->type = DT_COLORSPACE_SRGB;
216 else if(!strcmp(o->iccprofile, "linear_rec709_rgb") || !strcmp(o->iccprofile, "linear_rgb"))
218 else if(!strcmp(o->iccprofile, "linear_rec2020_rgb"))
220 else if(!strcmp(o->iccprofile, "adobergb"))
222 else if(!strcmp(o->iccprofile, "X profile"))
223 n->type = DT_COLORSPACE_DISPLAY;
224 else
225 {
226 n->type = DT_COLORSPACE_FILE;
227 g_strlcpy(n->filename, o->iccprofile, sizeof(n->filename));
228 }
229
230 n->intent = o->intent;
231
232 return 0;
233 }
234 if(old_version == 4 && new_version == 5)
235 {
236 typedef struct dt_iop_colorout_params_v4_t
237 {
239 char filename[DT_IOP_COLOR_ICC_LEN_V4];
241 } dt_iop_colorout_params_v4_t;
242
243
244 dt_iop_colorout_params_v4_t *o = (dt_iop_colorout_params_v4_t *)old_params;
246 memset(n, 0, sizeof(dt_iop_colorout_params_t));
247
248 n->type = o->type;
249 g_strlcpy(n->filename, o->filename, sizeof(n->filename));
250 n->intent = o->intent;
251
252 return 0;
253 }
254
255 return 1;
256#undef DT_IOP_COLOR_ICC_LEN_V4
257}
258
260{
261 const int program = 2; // basic.cl, from programs.conf
264 if(IS_NULL_PTR(gd)) return;
265 module->data = gd;
266 gd->kernel_colorout = dt_opencl_create_kernel(program, "colorout");
267}
268
275
278 float *const restrict out, const size_t npixels)
279{
280 const int run_lut0 = d->lut[0][0] >= 0.0f;
281 const int run_lut1 = d->lut[1][0] >= 0.0f;
282 const int run_lut2 = d->lut[2][0] >= 0.0f;
283 if(!(run_lut0 || run_lut1 || run_lut2)) return;
284
285 const float *const lut0 = d->lut[0];
286 const float *const lut1 = d->lut[1];
287 const float *const lut2 = d->lut[2];
288 const float *const coeff0 = d->unbounded_coeffs[0];
289 const float *const coeff1 = d->unbounded_coeffs[1];
290 const float *const coeff2 = d->unbounded_coeffs[2];
291
292 if(run_lut0 && run_lut1 && run_lut2)
293 {
295 for(size_t k = 0; k < npixels; k++)
296 {
297 const size_t idx = 4 * k;
298 out[idx + 0] = dt_ioppr_eval_trc(out[idx + 0], lut0, coeff0, LUT_SAMPLES);
299 out[idx + 1] = dt_ioppr_eval_trc(out[idx + 1], lut1, coeff1, LUT_SAMPLES);
300 out[idx + 2] = dt_ioppr_eval_trc(out[idx + 2], lut2, coeff2, LUT_SAMPLES);
301 }
302 }
303 else
304 {
306 for(size_t k = 0; k < npixels; k++)
307 {
308 const size_t idx = 4 * k;
309 if(run_lut0) out[idx + 0] = dt_ioppr_eval_trc(out[idx + 0], lut0, coeff0, LUT_SAMPLES);
310 if(run_lut1) out[idx + 1] = dt_ioppr_eval_trc(out[idx + 1], lut1, coeff1, LUT_SAMPLES);
311 if(run_lut2) out[idx + 2] = dt_ioppr_eval_trc(out[idx + 2], lut2, coeff2, LUT_SAMPLES);
312 }
313 }
314}
315
316#ifdef HAVE_OPENCL
317int 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)
318{
319 const dt_iop_roi_t *const roi_in = &piece->roi_in;
322 cl_mem dev_m = NULL, dev_r = NULL, dev_g = NULL, dev_b = NULL, dev_coeffs = NULL;
323
324 cl_int err = -999;
325 const int devid = pipe->devid;
326 const int width = roi_in->width;
327 const int height = roi_in->height;
328
329 if(d->type == DT_COLORSPACE_LAB)
330 {
331 size_t origin[] = { 0, 0, 0 };
332 size_t region[] = { roi_in->width, roi_in->height, 1 };
333 err = dt_opencl_enqueue_copy_image(devid, dev_in, dev_out, origin, origin, region);
334 if(err != CL_SUCCESS) goto error;
335 return TRUE;
336 }
337
338 size_t sizes[] = { ROUNDUPDWD(width, devid), ROUNDUPDHT(height, devid), 1 };
339
340 float cmatrix[12];
341 pack_3xSSE_to_3x4(d->cmatrix, cmatrix);
342 dev_m = dt_opencl_copy_host_to_device_constant(devid, sizeof(float) * 12, cmatrix);
343 if(IS_NULL_PTR(dev_m)) goto error;
344 dev_r = dt_opencl_copy_host_to_device(devid, d->lut[0], 256, 256, sizeof(float));
345 if(IS_NULL_PTR(dev_r)) goto error;
346 dev_g = dt_opencl_copy_host_to_device(devid, d->lut[1], 256, 256, sizeof(float));
347 if(IS_NULL_PTR(dev_g)) goto error;
348 dev_b = dt_opencl_copy_host_to_device(devid, d->lut[2], 256, 256, sizeof(float));
349 if(IS_NULL_PTR(dev_b)) goto error;
350 dev_coeffs
351 = dt_opencl_copy_host_to_device_constant(devid, sizeof(float) * 3 * 3, (float *)d->unbounded_coeffs);
352 if(IS_NULL_PTR(dev_coeffs)) goto error;
353 dt_opencl_set_kernel_arg(devid, gd->kernel_colorout, 0, sizeof(cl_mem), (void *)&dev_in);
354 dt_opencl_set_kernel_arg(devid, gd->kernel_colorout, 1, sizeof(cl_mem), (void *)&dev_out);
355 dt_opencl_set_kernel_arg(devid, gd->kernel_colorout, 2, sizeof(int), (void *)&width);
356 dt_opencl_set_kernel_arg(devid, gd->kernel_colorout, 3, sizeof(int), (void *)&height);
357 dt_opencl_set_kernel_arg(devid, gd->kernel_colorout, 4, sizeof(cl_mem), (void *)&dev_m);
358 dt_opencl_set_kernel_arg(devid, gd->kernel_colorout, 5, sizeof(cl_mem), (void *)&dev_r);
359 dt_opencl_set_kernel_arg(devid, gd->kernel_colorout, 6, sizeof(cl_mem), (void *)&dev_g);
360 dt_opencl_set_kernel_arg(devid, gd->kernel_colorout, 7, sizeof(cl_mem), (void *)&dev_b);
361 dt_opencl_set_kernel_arg(devid, gd->kernel_colorout, 8, sizeof(cl_mem), (void *)&dev_coeffs);
362 err = dt_opencl_enqueue_kernel_2d(devid, gd->kernel_colorout, sizes);
363 if(err != CL_SUCCESS) goto error;
369
370 return TRUE;
371
372error:
378 dt_print(DT_DEBUG_OPENCL, "[opencl_colorout] couldn't enqueue kernel! %d\n", err);
379 return FALSE;
380}
381#endif
382
384static inline void process_fastpath_matrix(const float *const restrict in, float *const restrict out,
385 const size_t npixels,
386 const dt_aligned_pixel_simd_t cm0,
387 const dt_aligned_pixel_simd_t cm1,
388 const dt_aligned_pixel_simd_t cm2,
389 const int use_nontemporal)
390{
391 __OMP_PARALLEL_FOR_SIMD__(aligned(in, out:64))
392 for(size_t k = 0; k < npixels; k++)
393 {
394 const size_t idx = 4 * k;
395 const dt_aligned_pixel_simd_t vin = dt_load_simd_aligned(in + idx);
396 const dt_aligned_pixel_simd_t vout = dt_mat3x4_mul_vec4(vin, cm0, cm1, cm2);
397 if(use_nontemporal)
398 dt_store_simd_nontemporal(out + idx, vout);
399 else
400 dt_store_simd_aligned(out + idx, vout);
401 }
402
403 if(use_nontemporal)
404 dt_omploop_sfence(); // ensure that nontemporal writes complete before we attempt to read output
405}
406
408int process(struct dt_iop_module_t *self, const dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece, const void *const ivoid,
409 void *const ovoid)
410{
411 const dt_iop_roi_t *const roi_out = &piece->roi_out;
412 const dt_iop_colorout_data_t *const d = (dt_iop_colorout_data_t *)piece->data;
413 const int gamutcheck = (d->mode == DT_PROFILE_GAMUTCHECK);
414 const size_t npixels = (size_t)roi_out->width * roi_out->height;
415 float *const restrict out = (float *)ovoid;
416
417 if(d->type == DT_COLORSPACE_LAB)
418 {
419 dt_iop_image_copy_by_size(ovoid, ivoid, roi_out->width, roi_out->height, 4);
420 }
421 else if(!isnan(d->cmatrix[0][0]))
422 {
423 const float *const restrict in = DT_IS_ALIGNED(ivoid);
424 float *const restrict out_aligned = DT_IS_ALIGNED(out);
425 const int use_nontemporal
426 = (d->lut[0][0] < 0.0f) && (d->lut[1][0] < 0.0f) && (d->lut[2][0] < 0.0f);
427 dt_colormatrix_t cmatrix;
428 transpose_3xSSE(d->cmatrix, cmatrix);
429 const dt_aligned_pixel_simd_t cm0 = dt_colormatrix_row_to_simd(cmatrix, 0);
430 const dt_aligned_pixel_simd_t cm1 = dt_colormatrix_row_to_simd(cmatrix, 1);
431 const dt_aligned_pixel_simd_t cm2 = dt_colormatrix_row_to_simd(cmatrix, 2);
432
433 process_fastpath_matrix(in, out_aligned, npixels, cm0, cm1, cm2, use_nontemporal);
434 process_fastpath_apply_tonecurves(d, out_aligned, npixels);
435 }
436 else
437 {
438// fprintf(stderr,"Using xform codepath\n");
439 /* Alias the LCMS transform before the OpenMP region and share that alias
440 * explicitly instead of reaching through `d->xform` inside the loop. */
441 const cmsHTRANSFORM xform = d->xform;
443 for(int k = 0; k < roi_out->height; k++)
444 {
445 const float *in = ((float *)ivoid) + (size_t)4 * k * roi_out->width;
446 float *const restrict outp = out + (size_t)4 * k * roi_out->width;
447
448 dt_colorspaces_transform_rgba_float_row(xform, in, outp, roi_out->width);
449
450 if(gamutcheck)
451 {
452 for(int j = 0; j < roi_out->width; j++)
453 {
454 if(outp[4*j+0] < 0.0f || outp[4*j+1] < 0.0f || outp[4*j+2] < 0.0f)
455 {
456 outp[4*j+0] = 0.0f;
457 outp[4*j+1] = 1.0f;
458 outp[4*j+2] = 1.0f;
459 }
460 }
461 }
462 }
463 }
464
466 dt_iop_alpha_copy(ivoid, ovoid, roi_out->width, roi_out->height);
467 return 0;
468}
469
470static cmsHPROFILE _make_clipping_profile(cmsHPROFILE profile)
471{
472 cmsUInt32Number size;
473 cmsHPROFILE old_profile = profile;
474 profile = NULL;
475
476 if(old_profile && cmsSaveProfileToMem(old_profile, NULL, &size))
477 {
478 char *data = malloc(size);
479
480 if(cmsSaveProfileToMem(old_profile, data, &size))
481 profile = cmsOpenProfileFromMem(data, size);
482
483 dt_free(data);
484 }
485
486 return profile;
487}
488
491{
494
495 d->type = p->type;
496
497 const int force_lcms2 = dt_conf_get_bool("plugins/lighttable/export/force_lcms2");
498
500 gchar *out_filename = NULL;
504 const char *work_filename = "";
505
506 cmsHPROFILE input = NULL;
507 cmsHPROFILE output = NULL;
508 cmsHPROFILE softproof = NULL;
509 cmsUInt32Number output_format = TYPE_RGBA_FLT;
510
512
513 // Softproof and gamut check take input from GUI and don't write it in internal parameters.
514 // The cacheline integrity hash will not be meaningful in this scenario,
515 // we need to bypass the cache entirely in these modes.
517
518 if(!IS_NULL_PTR(d->xform))
519 {
520 cmsDeleteTransform(d->xform);
521 d->xform = NULL;
522 }
523 d->cmatrix[0][0] = NAN;
524 d->lut[0][0] = -1.0f;
525 d->lut[1][0] = -1.0f;
526 d->lut[2][0] = -1.0f;
527 piece->process_cl_ready = 1;
528
529 /* if we are exporting then check and set usage of override profile */
530 if(pipe->type == DT_DEV_PIXELPIPE_EXPORT)
531 {
532 if(pipe->icc_type != DT_COLORSPACE_NONE)
533 {
534 // User defined explicitly a color space in export box: use that
535 p->type = pipe->icc_type;
536 g_strlcpy(p->filename, pipe->icc_filename, sizeof(p->filename));
537
538 }
539 else
540 {
541 // No color space defined : save with input profile
543 if(icc_input)
544 {
545 p->type = icc_input->type;
546 g_strlcpy(p->filename, icc_input->filename, sizeof(p->filename));
547 }
548 }
549
550 if((unsigned int)pipe->icc_intent < DT_INTENT_LAST)
551 {
552 // User defined explicitly an intent in export box: use that
553 p->intent = pipe->icc_intent;
554 }
555 else
556 {
557 // No intent defined : save with input intent
559 if(icc_input) p->intent = icc_input->intent;
560 }
561
562 out_type = p->type;
563 out_filename = p->filename;
564 out_intent = p->intent;
565 }
566 else if(pipe->type == DT_DEV_PIXELPIPE_THUMBNAIL)
567 {
568 out_type = DT_COLORSPACE_ADOBERGB;
569 out_filename = "";
571 }
572 else
573 {
574 /* we are not exporting, using display profile as output */
578 }
579
580 // when the output type is Lab then process is a nop, so we can avoid creating a transform
581 // and the subsequent error messages
582 d->type = out_type;
583 /* The virtual pipe mirrors history only for GUI geometry queries. It must
584 * still commit module contracts so ROI transforms see the same enabled
585 * modules, but creating an LCMS output transform for a pipe that will never
586 * process pixels only adds lifetime coupling to display/softproof profiles. */
587 if(!IS_NULL_PTR(pipe->dev) && pipe->dev->virtual_pipe == pipe)
588 {
589 d->type = DT_COLORSPACE_LAB;
590 piece->process_cl_ready = 0;
591 return;
592 }
593
594 if(out_type == DT_COLORSPACE_LAB)
595 return;
596
597 // Resolve the working profile currently carried by the pipe.
598 if(!IS_NULL_PTR(work_profile))
599 {
600 work_type = work_profile->type;
601 work_filename = work_profile->filename;
602 }
603 else
604 {
605 dt_ioppr_get_work_profile_type(self->dev, &work_type, &work_filename);
606 }
607
608 /*
609 * Setup transform flags
610 */
611 uint32_t transformFlags = 0;
612
613 /* creating output profile */
614 if(out_type == DT_COLORSPACE_DISPLAY)
615 pthread_rwlock_rdlock(&darktable.color_profiles->xprofile_lock);
616
617 const dt_colorspaces_color_profile_t *out_profile
618 = dt_colorspaces_get_profile(out_type, out_filename,
621 if(!IS_NULL_PTR(out_profile))
622 {
623 // Path for internal profile or external ICC file
624 output = out_profile->profile;
626 }
627 else if(pipe->type == DT_DEV_PIXELPIPE_EXPORT)
628 {
629 // Export with no explicit profile specified : use input file embedded profile
630 gboolean new_profile;
631 output = dt_colorspaces_get_embedded_profile(pipe->dev->image_storage.id, &out_type, &new_profile);
632 }
633
634 // We don't have an internal, embedded or external file profile,
635 // just fall back to sRGB
636 if(IS_NULL_PTR(output))
637 {
641 ->profile;
642 dt_control_log(_("missing output profile has been replaced by sRGB!"));
643 fprintf(stderr, "missing output profile `%s' has been replaced by sRGB!\n",
644 dt_colorspaces_get_name(out_type, out_filename));
645 }
646
647 if(work_type != DT_COLORSPACE_NONE)
648 {
649 const dt_colorspaces_color_profile_t *in_profile
650 = dt_colorspaces_get_profile(work_type, work_filename ? work_filename : "", DT_PROFILE_DIRECTION_ANY);
651 if(in_profile) input = in_profile->profile;
652 }
653 if(IS_NULL_PTR(input))
654 {
655 input = output;
657 "[colorout] could not resolve pipeline work profile, assuming input is already in output profile\n");
658 }
659
660 /* creating softproof profile if softproof is enabled */
661 if(d->mode != DT_PROFILE_NORMAL && pipe->type == DT_DEV_PIXELPIPE_FULL)
662 {
667
668 if(!IS_NULL_PTR(prof))
669 softproof = prof->profile;
670 else
671 {
675 ->profile;
676 dt_control_log(_("missing softproof profile has been replaced by sRGB!"));
677 fprintf(stderr, "missing softproof profile `%s' has been replaced by sRGB!\n",
680 }
681
682 // some of our internal profiles are what lcms considers ideal profiles as they have a parametric TRC so
683 // taking a roundtrip through those profiles during softproofing has no effect. as a workaround we have to
684 // make lcms quantisize those gamma tables to get the desired effect.
685 // in case that fails we don't enable softproofing.
686 softproof = _make_clipping_profile(softproof);
687 if(softproof)
688 {
689 /* TODO: the use of bpc should be userconfigurable either from module or preference pane */
690 /* softproof flag and black point compensation */
691 transformFlags |= cmsFLAGS_SOFTPROOFING | cmsFLAGS_NOCACHE | cmsFLAGS_BLACKPOINTCOMPENSATION;
692
693 if(d->mode == DT_PROFILE_GAMUTCHECK) transformFlags |= cmsFLAGS_GAMUTCHECK;
694 }
695 }
696
697 /*
698 * NOTE: theoretically, we should be passing
699 * UsedDirection = LCMS_USED_AS_PROOF into
700 * dt_colorspaces_get_matrix_from_output_profile() so that
701 * dt_colorspaces_get_matrix_from_profile() knows it, but since we do not try
702 * to use our matrix codepath when softproof is enabled, this seemed redundant.
703 */
704
705 const gboolean can_use_fast_matrix = (d->mode == DT_PROFILE_NORMAL
706 && !force_lcms2
707 && work_profile
708 && !work_profile->nonlinearlut
709 && !isnan(work_profile->matrix_in[0][0]));
710
711 // matrix fast path: work RGB -> XYZ from work profile, then XYZ -> output RGB from output profile
712 if(can_use_fast_matrix)
713 {
714 dt_colormatrix_t output_matrix;
715 if(!dt_colorspaces_get_matrix_from_output_profile(output, output_matrix, d->lut[0], d->lut[1], d->lut[2],
717 dt_colormatrix_mul(d->cmatrix, output_matrix, work_profile->matrix_in);
718 else
719 d->cmatrix[0][0] = NAN;
720 }
721
722 if(isnan(d->cmatrix[0][0]))
723 {
724 d->cmatrix[0][0] = NAN;
725 piece->process_cl_ready = 0;
726 d->xform = cmsCreateProofingTransform(input, TYPE_RGBA_FLT, output, output_format, softproof,
727 out_intent, INTENT_RELATIVE_COLORIMETRIC, transformFlags);
728 }
729
730 // user selected a non-supported output profile, check that:
731 if(IS_NULL_PTR(d->xform) && isnan(d->cmatrix[0][0]))
732 {
733 const char *const unsupported_name
734 = out_profile ? out_profile->name : dt_colorspaces_get_name(out_type, out_filename);
735 dt_control_log(_("unsupported output profile has been replaced by sRGB!"));
736 fprintf(stderr, "unsupported output profile `%s' has been replaced by sRGB!\n", unsupported_name);
738
739 if(can_use_fast_matrix)
740 {
741 dt_colormatrix_t output_matrix;
742 if(!dt_colorspaces_get_matrix_from_output_profile(output, output_matrix, d->lut[0], d->lut[1], d->lut[2],
744 dt_colormatrix_mul(d->cmatrix, output_matrix, work_profile->matrix_in);
745 else
746 d->cmatrix[0][0] = NAN;
747 }
748
749 if(isnan(d->cmatrix[0][0]))
750 {
751 d->cmatrix[0][0] = NAN;
752 piece->process_cl_ready = 0;
753
754 d->xform = cmsCreateProofingTransform(input, TYPE_RGBA_FLT, output, output_format, softproof,
755 out_intent, INTENT_RELATIVE_COLORIMETRIC, transformFlags);
756 }
757 }
758
759 if(out_type == DT_COLORSPACE_DISPLAY)
760 pthread_rwlock_unlock(&darktable.color_profiles->xprofile_lock);
761
762 // now try to initialize unbounded mode:
763 // we do extrapolation for input values above 1.0f.
764 // unfortunately we can only do this if we got the computation
765 // in our hands, i.e. for the fast builtin-dt-matrix-profile path.
766 for(int k = 0; k < 3; k++)
767 {
768 // omit luts marked as linear (negative as marker)
769 if(d->lut[k][0] >= 0.0f)
770 {
771 const float x[4] = { 0.7f, 0.8f, 0.9f, 1.0f };
772 const float y[4] = { extrapolate_lut(d->lut[k], x[0], LUT_SAMPLES),
773 extrapolate_lut(d->lut[k], x[1], LUT_SAMPLES),
774 extrapolate_lut(d->lut[k], x[2], LUT_SAMPLES),
775 extrapolate_lut(d->lut[k], x[3], LUT_SAMPLES) };
776 dt_iop_estimate_exp(x, y, 4, d->unbounded_coeffs[k]);
777 }
778 else
779 d->unbounded_coeffs[k][0] = -1.0f;
780 }
781
782 // softproof is never the original but always a copy that went through _make_clipping_profile()
784
785 dt_ioppr_set_pipe_output_profile_info(self->dev, pipe, d->type, out_filename, p->intent);
786}
787
789 const dt_dev_pixelpipe_iop_t *piece)
790{
791 (void)self;
792 (void)pipe;
793 (void)piece;
794 return TRUE;
795}
796
798{
800 piece->data_size = sizeof(dt_iop_colorout_data_t);
801}
802
804{
806 if(!IS_NULL_PTR(d->xform))
807 {
808 cmsDeleteTransform(d->xform);
809 d->xform = NULL;
810 }
811
812 dt_free_align(piece->data);
813 piece->data = NULL;
814}
815
816
818{
819 dt_iop_default_init(module);
820
821 module->hide_enable_button = 1;
822 module->default_enabled = 1;
823}
824
827
829
831{
832 IOP_GUI_ALLOC(colorout);
833 self->widget = gtk_label_new(NULL);
834 gtk_label_set_markup(GTK_LABEL(self->widget),_("Convert images to the display or export RGB color space. "
835 "The color profile is set in the export module or in the display preferences. "));
836 gtk_widget_set_halign(self->widget, GTK_ALIGN_START);
837 gtk_label_set_xalign (GTK_LABEL(self->widget), 0.0f);
838 gtk_label_set_line_wrap(GTK_LABEL(self->widget), TRUE);
839}
840
841
842// clang-format off
843// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
844// vim: shiftwidth=2 expandtab tabstop=2 cindent
845// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
846// 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
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
dt_iop_colorspace_type_t
@ IOP_CS_RGB
@ IOP_CS_LAB
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)
Definition colorout.c:489
void init(dt_iop_module_t *module)
Definition colorout.c:817
const char ** description(struct dt_iop_module_t *self)
Definition colorout.c:120
int default_group()
Definition colorout.c:131
static dt_iop_colorspace_type_t _colorout_input_format_cst(dt_iop_module_t *self, const dt_dev_pixelpipe_t *pipe)
Definition colorout.c:146
__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)
Definition colorout.c:408
void init_pipe(struct dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
Definition colorout.c:797
const char * name()
Definition colorout.c:114
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)
Definition colorout.c:176
#define DT_IOP_COLOR_ICC_LEN
Definition colorout.c:85
void gui_init(dt_iop_module_t *self)
Definition colorout.c:830
static dt_iop_colorspace_type_t _colorout_output_format_cst(dt_iop_module_t *self, const dt_dev_pixelpipe_t *pipe)
Definition colorout.c:161
void cleanup_global(dt_iop_module_so_t *module)
Definition colorout.c:269
int default_colorspace(dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece)
Definition colorout.c:141
void input_format(dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece, dt_iop_buffer_dsc_t *dsc)
Definition colorout.c:168
int flags()
Definition colorout.c:136
dt_iop_colorout_gui_data_t dummy
Definition colorout.c:828
static __DT_CLONE_TARGETS__ void process_fastpath_matrix(const float *const restrict in, float *const restrict out, const size_t npixels, const dt_aligned_pixel_simd_t cm0, const dt_aligned_pixel_simd_t cm1, const dt_aligned_pixel_simd_t cm2, const int use_nontemporal)
Definition colorout.c:384
void cleanup_pipe(struct dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
Definition colorout.c:803
void init_global(dt_iop_module_so_t *module)
Definition colorout.c:259
static cmsHPROFILE _make_clipping_profile(cmsHPROFILE profile)
Definition colorout.c:470
static __DT_CLONE_TARGETS__ void process_fastpath_apply_tonecurves(const dt_iop_colorout_data_t *const d, float *const restrict out, const size_t npixels)
Definition colorout.c:277
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)
Definition colorout.c:317
int legacy_params(dt_iop_module_t *self, const void *const old_params, const int old_version, void *new_params, const int new_version)
Definition colorout.c:184
#define LUT_SAMPLES
Definition colorout.c:86
#define DT_IOP_COLOR_ICC_LEN_V4
gboolean runtime_data_hash(struct dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece)
Definition colorout.c:788
const dt_colorspaces_color_profile_t * dt_colorspaces_get_profile(dt_colorspaces_color_profile_type_t type, const char *filename, dt_colorspaces_profile_direction_t direction)
int dt_colorspaces_get_matrix_from_output_profile(cmsHPROFILE prof, dt_colormatrix_t matrix, float *lutr, float *lutg, float *lutb, const int lutsize)
const cmsHPROFILE dt_colorspaces_get_embedded_profile(const int32_t imgid, dt_colorspaces_color_profile_type_t *type, gboolean *new_profile)
void dt_colorspaces_cleanup_profile(cmsHPROFILE p)
const char * dt_colorspaces_get_name(dt_colorspaces_color_profile_type_t type, const char *filename)
void dt_colorspaces_transform_rgba_float_row(const cmsHTRANSFORM transform, const float *in, float *out, const int width)
dt_iop_color_intent_t
Definition colorspaces.h:63
@ DT_INTENT_PERCEPTUAL
Definition colorspaces.h:64
@ DT_INTENT_LAST
Definition colorspaces.h:68
#define TYPE_XYZA_FLT
Definition colorspaces.h:53
dt_colorspaces_color_profile_type_t
Definition colorspaces.h:81
@ DT_COLORSPACE_ADOBERGB
Definition colorspaces.h:85
@ DT_COLORSPACE_FILE
Definition colorspaces.h:83
@ DT_COLORSPACE_DISPLAY
Definition colorspaces.h:91
@ DT_COLORSPACE_SRGB
Definition colorspaces.h:84
@ DT_COLORSPACE_LAB
Definition colorspaces.h:89
@ DT_COLORSPACE_NONE
Definition colorspaces.h:82
@ DT_COLORSPACE_LIN_REC2020
Definition colorspaces.h:87
@ DT_COLORSPACE_XYZ
Definition colorspaces.h:88
@ DT_COLORSPACE_LIN_REC709
Definition colorspaces.h:86
@ DT_PROFILE_DIRECTION_OUT
@ DT_PROFILE_DIRECTION_DISPLAY
@ DT_PROFILE_DIRECTION_ANY
dt_colorspaces_color_mode_t
@ DT_PROFILE_GAMUTCHECK
@ DT_PROFILE_NORMAL
const dt_colormatrix_t dt_aligned_pixel_t out
dt_store_simd_aligned(out, dt_mat3x4_mul_vec4(vin, dt_colormatrix_row_to_simd(matrix, 0), dt_colormatrix_row_to_simd(matrix, 1), dt_colormatrix_row_to_simd(matrix, 2)))
typedef void((*dt_cache_allocate_t)(void *userdata, dt_cache_entry_t *entry))
int type
int dt_conf_get_bool(const char *name)
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
@ DT_DEBUG_DEV
Definition darktable.h:717
#define DT_IS_ALIGNED(x)
Definition darktable.h:371
#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 __OMP_PARALLEL_FOR_SIMD__(...)
Definition darktable.h:259
#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
void dt_iop_params_t
Definition dev_history.h:41
@ DT_DEV_PIXELPIPE_DISPLAY_MASK
Definition develop.h:118
@ TYPE_FLOAT
Definition format.h:46
static void dt_iop_image_copy_by_size(float *const __restrict__ out, const float *const __restrict__ in, const size_t width, const size_t height, const size_t ch)
Definition imagebuf.h:87
void dt_iop_default_init(dt_iop_module_t *module)
Definition imageop.c:316
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
void dt_iop_set_cache_bypass(dt_iop_module_t *module, gboolean state)
Definition imageop.c:2915
#define dt_omploop_sfence()
Definition imageop.h:702
@ IOP_FLAGS_ALLOW_TILING
Definition imageop.h:169
@ IOP_FLAGS_ONE_INSTANCE
Definition imageop.h:172
@ IOP_FLAGS_NO_HISTORY_STACK
Definition imageop.h:174
@ IOP_CS_RGB_DISPLAY
Definition imageop.h:210
@ IOP_GROUP_TECHNICAL
Definition imageop.h:143
#define IOP_GUI_ALLOC(module)
Definition imageop.h:599
void *const ovoid
static void dt_iop_estimate_exp(const float *const x, const float *const y, const int num, float *coeff)
void dt_ioppr_get_work_profile_type(struct dt_develop_t *dev, dt_colorspaces_color_profile_type_t *profile_type, const char **profile_filename)
dt_iop_order_iccprofile_info_t * dt_ioppr_get_pipe_work_profile_info(const struct dt_dev_pixelpipe_t *pipe)
dt_iop_order_iccprofile_info_t * dt_ioppr_set_pipe_output_profile_info(struct dt_develop_t *dev, struct dt_dev_pixelpipe_t *pipe, const dt_colorspaces_color_profile_type_t type, const char *filename, const int intent)
dt_iop_order_iccprofile_info_t * dt_ioppr_get_pipe_input_profile_info(const struct dt_dev_pixelpipe_t *pipe)
static const float x
const float *const lut
static dt_aligned_pixel_t float *const const float unbounded_coeffs[3][3]
float *const restrict const size_t k
float DT_ALIGNED_ARRAY dt_colormatrix_t[4][4]
Definition matrices.h:33
static void transpose_3xSSE(const dt_colormatrix_t input, dt_colormatrix_t output)
Definition matrices.h:68
static void pack_3xSSE_to_3x4(const dt_colormatrix_t input, float output[12])
Definition matrices.h:149
static void dt_colormatrix_mul(dt_colormatrix_t dst, const dt_colormatrix_t m1, const dt_colormatrix_t m2)
Definition matrices.h:166
size_t size
Definition mipmap_cache.c:3
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
int dt_opencl_enqueue_copy_image(const int devid, cl_mem src, cl_mem dst, size_t *orig_src, size_t *orig_dst, size_t *region)
Definition opencl.c:2261
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_copy_host_to_device(const int devid, void *host, const int width, const int height, const int bpp)
Definition opencl.c:2347
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
@ DT_DEV_PIXELPIPE_THUMBNAIL
Definition pixelpipe.h:41
@ DT_DEV_PIXELPIPE_EXPORT
Definition pixelpipe.h:38
@ DT_DEV_PIXELPIPE_FULL
Definition pixelpipe.h:39
struct dt_colorspaces_t * color_profiles
Definition darktable.h:788
dt_colorspaces_color_profile_type_t softproof_type
pthread_rwlock_t xprofile_lock
dt_colorspaces_color_mode_t mode
char softproof_filename[512]
dt_colorspaces_color_profile_type_t display_type
char display_filename[512]
dt_iop_color_intent_t display_intent
struct dt_iop_module_t *void * data
dt_colorspaces_color_profile_type_t icc_type
dt_iop_color_intent_t icc_intent
dt_dev_pixelpipe_type_t type
struct dt_develop_t * dev
dt_image_t image_storage
Definition develop.h:259
struct dt_dev_pixelpipe_t * virtual_pipe
Definition develop.h:251
int32_t id
Definition image.h:319
unsigned int channels
Definition format.h:54
dt_iop_buffer_type_t datatype
Definition format.h:56
dt_colorspaces_color_mode_t mode
Definition colorout.c:93
cmsHTRANSFORM xform
Definition colorout.c:96
dt_colormatrix_t cmatrix
Definition colorout.c:95
dt_colorspaces_color_profile_type_t type
Definition colorout.c:92
dt_iop_color_intent_t intent
Definition colorout.c:109
dt_colorspaces_color_profile_type_t type
Definition colorout.c:107
dt_iop_global_data_t * data
Definition imageop.h:233
GtkWidget * widget
Definition imageop.h:337
struct dt_develop_t * dev
Definition imageop.h:296
dt_iop_global_data_t * global_data
Definition imageop.h:314
dt_iop_params_t * params
Definition imageop.h:307
dt_iop_color_intent_t intent
Definition iop_profile.h:55
dt_colorspaces_color_profile_type_t type
Definition iop_profile.h:53
char filename[DT_IOP_COLOR_ICC_LEN]
Definition iop_profile.h:54
Region of interest passed through the pixelpipe.
Definition imageop.h:72