Ansel 0.0
A darktable fork - bloat + design vision
Loading...
Searching...
No Matches
iop_profile.c
Go to the documentation of this file.
1/*
2 This file is part of darktable,
3 Copyright (C) 2018-2021, 2023, 2026 Aurélien PIERRE.
4 Copyright (C) 2018-2019 Edgardo Hoszowski.
5 Copyright (C) 2019 Aldric Renaudin.
6 Copyright (C) 2019 Andreas Schneider.
7 Copyright (C) 2019, 2022 Hanno Schwalm.
8 Copyright (C) 2019 Heiko Bauke.
9 Copyright (C) 2019 Jacques Le Clerc.
10 Copyright (C) 2019 jakubfi.
11 Copyright (C) 2019 luzpaz.
12 Copyright (C) 2019-2021 Pascal Obry.
13 Copyright (C) 2019, 2021 Philippe Weyland.
14 Copyright (C) 2019, 2021 Sakari Kapanen.
15 Copyright (C) 2019 Tobias Ellinghaus.
16 Copyright (C) 2020-2021 Dan Torop.
17 Copyright (C) 2020 Harold le Clément de Saint-Marcq.
18 Copyright (C) 2020 Hubert Kowalski.
19 Copyright (C) 2020-2021 Ralf Brown.
20 Copyright (C) 2021 paolodepetrillo.
21 Copyright (C) 2022 Martin Bařinka.
22 Copyright (C) 2022 Philipp Lutz.
23 Copyright (C) 2022 Victor Forsiuk.
24 Copyright (C) 2024 Alynx Zhou.
25
26 darktable is free software: you can redistribute it and/or modify
27 it under the terms of the GNU General Public License as published by
28 the Free Software Foundation, either version 3 of the License, or
29 (at your option) any later version.
30
31 darktable is distributed in the hope that it will be useful,
32 but WITHOUT ANY WARRANTY; without even the implied warranty of
33 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
34 GNU General Public License for more details.
35
36 You should have received a copy of the GNU General Public License
37 along with darktable. If not, see <http://www.gnu.org/licenses/>.
38*/
39#ifdef HAVE_CONFIG_H
40#include "config.h"
41#endif
42
43#include "common/colorspaces.h"
44#include "common/darktable.h"
45#include "common/iop_profile.h"
46#include "common/debug.h"
47#include "common/matrices.h"
48#include "develop/imageop.h"
50#include "develop/pixelpipe.h"
51#include "develop/develop.h"
52
53#include <assert.h>
54#include <stdio.h>
55#include <stdlib.h>
56#include <string.h>
57
58static inline __attribute__((always_inline)) void _mark_as_nonmatrix_profile(dt_iop_order_iccprofile_info_t *const profile_info)
59{
60 profile_info->matrix_in[0][0] = NAN;
61 profile_info->matrix_in_transposed[0][0] = NAN;
62 profile_info->matrix_out[0][0] = NAN;
63 profile_info->matrix_out_transposed[0][0] = NAN;
64}
65
67static void _clear_lut_curves(dt_iop_order_iccprofile_info_t *const profile_info)
68{
69 for(int i = 0; i < 3; i++)
70 {
71 profile_info->lut_in[i][0] = -1.0f;
72 profile_info->lut_out[i][0] = -1.0f;
73 }
74}
75
76static void _transform_from_to_rgb_lab_lcms2(const float *const image_in, float *const image_out, const int width,
78 const char *filename, const int intent, const int direction)
79{
80 cmsHTRANSFORM *xform = NULL;
81 cmsHPROFILE *rgb_profile = NULL;
82 cmsHPROFILE *lab_profile = NULL;
83
85 pthread_rwlock_rdlock(&darktable.color_profiles->xprofile_lock);
86
88 {
90 if(profile) rgb_profile = profile->profile;
91 }
92 else
94 if(rgb_profile)
95 {
96 cmsColorSpaceSignature rgb_color_space = cmsGetColorSpace(rgb_profile);
97 if(rgb_color_space != cmsSigRgbData)
98 {
99 fprintf(stderr, "working profile color space `%c%c%c%c' not supported\n",
100 (char)(rgb_color_space>>24),
101 (char)(rgb_color_space>>16),
102 (char)(rgb_color_space>>8),
103 (char)(rgb_color_space));
104 rgb_profile = NULL;
105 }
106 }
107 if(IS_NULL_PTR(rgb_profile))
108 {
110 fprintf(stderr, _("unsupported working profile %s has been replaced by Rec2020 RGB!\n"), filename);
111 }
112
114
115 cmsHPROFILE *input_profile = NULL;
116 cmsHPROFILE *output_profile = NULL;
117 cmsUInt32Number input_format = TYPE_RGBA_FLT;
118 cmsUInt32Number output_format = TYPE_LabA_FLT;
119
120 if(direction == 1) // rgb --> lab
121 {
122 input_profile = rgb_profile;
123 input_format = TYPE_RGBA_FLT;
124 output_profile = lab_profile;
125 output_format = TYPE_LabA_FLT;
126 }
127 else // lab -->rgb
128 {
129 input_profile = lab_profile;
130 input_format = TYPE_LabA_FLT;
131 output_profile = rgb_profile;
132 output_format = TYPE_RGBA_FLT;
133 }
134
135 xform = cmsCreateTransform(input_profile, input_format, output_profile, output_format, intent, 0);
136
138 pthread_rwlock_unlock(&darktable.color_profiles->xprofile_lock);
139
140 if(xform)
141 {
142 dt_colorspaces_transform_rgba_float_image(xform, image_in, image_out, width, height);
143 }
144 else
145 fprintf(stderr, "[_transform_from_to_rgb_lab_lcms2] cannot create transform\n");
146
147 if(xform) cmsDeleteTransform(xform);
148}
149
150static inline __attribute__((always_inline)) void _transform_rgb_to_rgb_lcms2(const float *const image_in, float *const image_out, const int width,
151 const int height, const dt_colorspaces_color_profile_type_t type_from,
152 const char *filename_from,
153 const dt_colorspaces_color_profile_type_t type_to, const char *filename_to,
154 const int intent)
155{
156 cmsHTRANSFORM *xform = NULL;
157 cmsHPROFILE *from_rgb_profile = NULL;
158 cmsHPROFILE *to_rgb_profile = NULL;
159
160 if(type_from == DT_COLORSPACE_DISPLAY || type_to == DT_COLORSPACE_DISPLAY)
161 pthread_rwlock_rdlock(&darktable.color_profiles->xprofile_lock);
162
163 if(type_from != DT_COLORSPACE_NONE)
164 {
165 const dt_colorspaces_color_profile_t *profile_from
166 = dt_colorspaces_get_profile(type_from, filename_from, DT_PROFILE_DIRECTION_ANY);
167 if(profile_from) from_rgb_profile = profile_from->profile;
168 }
169 else
170 {
171 fprintf(stderr, "[_transform_rgb_to_rgb_lcms2] invalid from profile\n");
172 }
173
174 if(type_to != DT_COLORSPACE_NONE)
175 {
176 const dt_colorspaces_color_profile_t *profile_to
178 if(profile_to) to_rgb_profile = profile_to->profile;
179 }
180 else
181 {
182 fprintf(stderr, "[_transform_rgb_to_rgb_lcms2] invalid to profile\n");
183 }
184
185 if(from_rgb_profile)
186 {
187 cmsColorSpaceSignature rgb_color_space = cmsGetColorSpace(from_rgb_profile);
188 if(rgb_color_space != cmsSigRgbData)
189 {
190 fprintf(stderr, "[_transform_rgb_to_rgb_lcms2] profile color space `%c%c%c%c' not supported\n",
191 (char)(rgb_color_space >> 24), (char)(rgb_color_space >> 16), (char)(rgb_color_space >> 8),
192 (char)(rgb_color_space));
193 from_rgb_profile = NULL;
194 }
195 }
196 if(to_rgb_profile)
197 {
198 cmsColorSpaceSignature rgb_color_space = cmsGetColorSpace(to_rgb_profile);
199 if(rgb_color_space != cmsSigRgbData)
200 {
201 fprintf(stderr, "[_transform_rgb_to_rgb_lcms2] profile color space `%c%c%c%c' not supported\n",
202 (char)(rgb_color_space >> 24), (char)(rgb_color_space >> 16), (char)(rgb_color_space >> 8),
203 (char)(rgb_color_space));
204 to_rgb_profile = NULL;
205 }
206 }
207
208 cmsHPROFILE *input_profile = NULL;
209 cmsHPROFILE *output_profile = NULL;
210 cmsUInt32Number input_format = TYPE_RGBA_FLT;
211 cmsUInt32Number output_format = TYPE_RGBA_FLT;
212
213 input_profile = from_rgb_profile;
214 input_format = TYPE_RGBA_FLT;
215 output_profile = to_rgb_profile;
216 output_format = TYPE_RGBA_FLT;
217
218 if(input_profile && output_profile)
219 xform = cmsCreateTransform(input_profile, input_format, output_profile, output_format, intent, 0);
220
221 if(type_from == DT_COLORSPACE_DISPLAY || type_to == DT_COLORSPACE_DISPLAY)
222 pthread_rwlock_unlock(&darktable.color_profiles->xprofile_lock);
223
224 if(xform)
225 {
226 dt_colorspaces_transform_rgba_float_image(xform, image_in, image_out, width, height);
227 }
228 else
229 fprintf(stderr, "[_transform_rgb_to_rgb_lcms2] cannot create transform\n");
230
231 if(xform) cmsDeleteTransform(xform);
232}
233
234static void _transform_lcms2(struct dt_iop_module_t *self, const float *const image_in, float *const image_out,
235 const int width, const int height, const int cst_from, const int cst_to,
236 int *converted_cst, const dt_iop_order_iccprofile_info_t *const profile_info)
237{
238 if(cst_from == cst_to)
239 {
240 *converted_cst = cst_to;
241 return;
242 }
243
244 *converted_cst = cst_to;
245
246 if(dt_iop_colorspace_is_rgb(cst_from) && cst_to == IOP_CS_LAB)
247 {
249 "[_transform_lcms2] transfoming from RGB to Lab (%s %s)\n", self->op, self->multi_name);
250 _transform_from_to_rgb_lab_lcms2(image_in, image_out, width, height, profile_info->type,
251 profile_info->filename, profile_info->intent, 1);
252 }
253 else if(cst_from == IOP_CS_LAB && dt_iop_colorspace_is_rgb(cst_to))
254 {
256 "[_transform_lcms2] transfoming from Lab to RGB (%s %s)\n", self->op, self->multi_name);
257 _transform_from_to_rgb_lab_lcms2(image_in, image_out, width, height, profile_info->type,
258 profile_info->filename, profile_info->intent, -1);
259 }
260 else
261 {
262 *converted_cst = cst_from;
263 fprintf(stderr, "[_transform_lcms2] invalid conversion from %i to %i\n", cst_from, cst_to);
264 }
265}
266
267static inline __attribute__((always_inline)) void _transform_lcms2_rgb(const float *const image_in, float *const image_out, const int width,
268 const int height,
269 const dt_iop_order_iccprofile_info_t *const profile_info_from,
270 const dt_iop_order_iccprofile_info_t *const profile_info_to)
271{
272 _transform_rgb_to_rgb_lcms2(image_in, image_out, width, height, profile_info_from->type,
273 profile_info_from->filename, profile_info_to->type, profile_info_to->filename,
274 profile_info_to->intent);
275}
276
277
278static inline int _init_unbounded_coeffs(float *const lutr, float *const lutg, float *const lutb,
279 float *const unbounded_coeffsr, float *const unbounded_coeffsg, float *const unbounded_coeffsb, const int lutsize)
280{
281 int nonlinearlut = 0;
282 float *lut[3] = { lutr, lutg, lutb };
283 float *unbounded_coeffs[3] = { unbounded_coeffsr, unbounded_coeffsg, unbounded_coeffsb };
284
285 for(int k = 0; k < 3; k++)
286 {
287 // omit luts marked as linear (negative as marker)
288 if(lut[k][0] >= 0.0f)
289 {
290 const dt_aligned_pixel_t x = { 0.7f, 0.8f, 0.9f, 1.0f };
291 const dt_aligned_pixel_t y = { extrapolate_lut(lut[k], x[0], lutsize),
292 extrapolate_lut(lut[k], x[1], lutsize),
293 extrapolate_lut(lut[k], x[2], lutsize),
294 extrapolate_lut(lut[k], x[3], lutsize) };
296
297 nonlinearlut++;
298 }
299 else
300 unbounded_coeffs[k][0] = -1.0f;
301 }
302
303 return nonlinearlut;
304}
305
306
307static inline void _apply_tonecurves(const float *const image_in, float *const image_out,
308 const int width, const int height,
309 const float *const restrict lutr,
310 const float *const restrict lutg,
311 const float *const restrict lutb,
312 const float *const restrict unbounded_coeffsr,
313 const float *const restrict unbounded_coeffsg,
314 const float *const restrict unbounded_coeffsb,
315 const int lutsize)
316{
317 const int ch = 4;
318 const float *const lut[3] = { lutr, lutg, lutb };
319 const float *const unbounded_coeffs[3] = { unbounded_coeffsr, unbounded_coeffsg, unbounded_coeffsb };
320 const size_t stride = (size_t)ch * width * height;
321
322 // do we have any lut to apply, or is this a linear profile?
323 if((lut[0][0] >= 0.0f) && (lut[1][0] >= 0.0f) && (lut[2][0] >= 0.0f))
324 {
325 __OMP_PARALLEL_FOR__(collapse(2))
326 for(size_t k = 0; k < stride; k += ch)
327 {
328 for(int c = 0; c < 3; c++) // for_each_channel doesn't vectorize, and some code needs image_out[3] preserved
329 {
330 image_out[k + c] = dt_ioppr_eval_trc(image_in[k + c], lut[c], unbounded_coeffs[c], lutsize);
331 }
332 }
333 }
334 else if((lut[0][0] >= 0.0f) || (lut[1][0] >= 0.0f) || (lut[2][0] >= 0.0f))
335 {
336 __OMP_PARALLEL_FOR__(collapse(2))
337 for(size_t k = 0; k < stride; k += ch)
338 {
339 for(int c = 0; c < 3; c++) // for_each_channel doesn't vectorize, and some code needs image_out[3] preserved
340 {
341 if(lut[c][0] >= 0.0f)
342 {
343 image_out[k + c] = dt_ioppr_eval_trc(image_in[k + c], lut[c], unbounded_coeffs[c], lutsize);
344 }
345 }
346 }
347 }
348}
349
350
352static inline void _transform_rgb_to_lab_matrix(const float *const restrict image_in, float *const restrict image_out,
353 const int width, const int height,
354 const dt_iop_order_iccprofile_info_t *const profile_info)
355{
356 const int ch = 4;
357 const size_t stride = (size_t)width * height * ch;
358 const dt_colormatrix_t *matrix_ptr = &profile_info->matrix_in_transposed;
359 const dt_aligned_pixel_simd_t m0 = dt_colormatrix_row_to_simd(*matrix_ptr, 0);
360 const dt_aligned_pixel_simd_t m1 = dt_colormatrix_row_to_simd(*matrix_ptr, 1);
361 const dt_aligned_pixel_simd_t m2 = dt_colormatrix_row_to_simd(*matrix_ptr, 2);
362
363 if(profile_info->nonlinearlut)
364 {
365 // TODO : maybe optimize that path like _transform_matrix_rgb
366 _apply_tonecurves(image_in, image_out, width, height, profile_info->lut_in[0], profile_info->lut_in[1],
367 profile_info->lut_in[2], profile_info->unbounded_coeffs_in[0],
368 profile_info->unbounded_coeffs_in[1], profile_info->unbounded_coeffs_in[2],
369 profile_info->lutsize);
370 __OMP_PARALLEL_FOR_SIMD__(aligned(image_out:64))
371 for(size_t y = 0; y < stride; y += ch)
372 {
373 float *const restrict in = __builtin_assume_aligned(image_out + y, 16);
375 const dt_aligned_pixel_simd_t vin = dt_load_simd_aligned(in);
376 dt_store_simd_aligned(xyz, dt_mat3x4_mul_vec4(vin, m0, m1, m2));
377 dt_XYZ_to_Lab(xyz, in);
378 }
379 }
380 else
381 {
382 __OMP_PARALLEL_FOR_SIMD__(aligned(image_in, image_out:64))
383 for(size_t y = 0; y < stride; y += ch)
384 {
385 const float *const restrict in = __builtin_assume_aligned(image_in + y, 16);
386 float *const restrict out = __builtin_assume_aligned(image_out + y, 16);
387
389 const dt_aligned_pixel_simd_t vin = dt_load_simd_aligned(in);
390 dt_store_simd_aligned(xyz, dt_mat3x4_mul_vec4(vin, m0, m1, m2));
391 dt_XYZ_to_Lab(xyz, out);
392 }
393 }
394}
395
396
398static inline void _transform_lab_to_rgb_matrix(const float *const image_in, float *const image_out, const int width,
399 const int height,
400 const dt_iop_order_iccprofile_info_t *const profile_info)
401{
402 const int ch = 4;
403 const size_t stride = (size_t)width * height * ch;
404 const int use_nontemporal = !profile_info->nonlinearlut;
405 const dt_colormatrix_t *matrix_ptr = &profile_info->matrix_out_transposed;
406 const dt_aligned_pixel_simd_t m0 = dt_colormatrix_row_to_simd(*matrix_ptr, 0);
407 const dt_aligned_pixel_simd_t m1 = dt_colormatrix_row_to_simd(*matrix_ptr, 1);
408 const dt_aligned_pixel_simd_t m2 = dt_colormatrix_row_to_simd(*matrix_ptr, 2);
410 for(size_t y = 0; y < stride; y += ch)
411 {
412 const float *const restrict in = __builtin_assume_aligned(image_in + y, 16);
413 float *const restrict out = __builtin_assume_aligned(image_out + y, 16);
414
416 const float alpha = in[3]; // some code does in-place conversions and relies on alpha being preserved
417 dt_Lab_to_XYZ(in, xyz);
418 const dt_aligned_pixel_simd_t vxyz = dt_load_simd_aligned(xyz);
419 dt_aligned_pixel_simd_t rgb = dt_mat3x4_mul_vec4(vxyz, m0, m1, m2);
420 rgb[3] = alpha;
421 if(use_nontemporal)
422 dt_store_simd_nontemporal(out, rgb);
423 else
425 }
426
427 if(use_nontemporal)
428 dt_omploop_sfence(); // ensure that nontemporal writes complete before we attempt to read output
429
430 if(profile_info->nonlinearlut)
431 {
432 // TODO : maybe optimize that path like _transform_matrix_rgb
433 _apply_tonecurves(image_out, image_out, width, height, profile_info->lut_out[0], profile_info->lut_out[1],
434 profile_info->lut_out[2], profile_info->unbounded_coeffs_out[0],
435 profile_info->unbounded_coeffs_out[1], profile_info->unbounded_coeffs_out[2],
436 profile_info->lutsize);
437 }
438}
439
440
442static inline void _transform_matrix_rgb(const float *const restrict image_in,
443 float *const restrict image_out,
444 const int width, const int height,
445 const dt_iop_order_iccprofile_info_t *const profile_info_from,
446 const dt_iop_order_iccprofile_info_t *const profile_info_to)
447{
448 const int ch = 4;
449 const size_t stride = (size_t)width * height * ch;
450
451 // RGB -> XYZ -> RGB are 2 matrices products, they can be premultiplied globally ahead
452 // and put in a new matrix. then we spare one matrix product per pixel.
453 dt_colormatrix_t _matrix;
454 dt_colormatrix_mul(_matrix, profile_info_to->matrix_out, profile_info_from->matrix_in);
456 transpose_3xSSE(_matrix, matrix);
457 const dt_aligned_pixel_simd_t m0 = dt_colormatrix_row_to_simd(matrix, 0);
458 const dt_aligned_pixel_simd_t m1 = dt_colormatrix_row_to_simd(matrix, 1);
459 const dt_aligned_pixel_simd_t m2 = dt_colormatrix_row_to_simd(matrix, 2);
460
461 if(profile_info_from->nonlinearlut || profile_info_to->nonlinearlut)
462 {
463 const int use_nontemporal = !profile_info_to->nonlinearlut;
464 const int run_lut_in[3] DT_ALIGNED_PIXEL= { (profile_info_from->lut_in[0][0] >= 0.0f),
465 (profile_info_from->lut_in[1][0] >= 0.0f),
466 (profile_info_from->lut_in[2][0] >= 0.0f) };
467
468 const int run_lut_out[3] DT_ALIGNED_PIXEL = { (profile_info_to->lut_out[0][0] >= 0.0f),
469 (profile_info_to->lut_out[1][0] >= 0.0f),
470 (profile_info_to->lut_out[2][0] >= 0.0f) };
472 for(size_t y = 0; y < stride; y += 4)
473 {
474 const float *const restrict in = __builtin_assume_aligned(image_in + y, 16);
475 float *const restrict out = __builtin_assume_aligned(image_out + y, 16);
477
478 // linearize if non-linear input
479 if(profile_info_from->nonlinearlut)
480 {
481 for(size_t c = 0; c < 3; c++)
482 {
483 rgb[c] = (run_lut_in[c]
484 ? dt_ioppr_eval_trc(in[c], profile_info_from->lut_in[c],
485 profile_info_from->unbounded_coeffs_in[c], profile_info_from->lutsize)
486 : in[c]);
487 }
488 }
489 else
490 {
492 rgb[c] = in[c];
493 }
494
495 if(profile_info_to->nonlinearlut)
496 {
497 // convert color space
499 const dt_aligned_pixel_simd_t vrgb = dt_load_simd_aligned(rgb);
500 dt_store_simd_aligned(temp, dt_mat3x4_mul_vec4(vrgb, m0, m1, m2));
501
502 // de-linearize non-linear output
503 for(size_t c = 0; c < 3; c++)
504 {
505 out[c] = (run_lut_out[c]
506 ? dt_ioppr_eval_trc(temp[c], profile_info_to->lut_out[c],
507 profile_info_to->unbounded_coeffs_out[c], profile_info_to->lutsize)
508 : temp[c]);
509 }
510 }
511 else
512 {
513 // convert color space
514 const dt_aligned_pixel_simd_t vrgb = dt_load_simd_aligned(rgb);
515 if(use_nontemporal)
516 dt_store_simd_nontemporal(out, dt_mat3x4_mul_vec4(vrgb, m0, m1, m2));
517 else
518 dt_store_simd_aligned(out, dt_mat3x4_mul_vec4(vrgb, m0, m1, m2));
519 }
520 }
521
522 if(use_nontemporal)
523 dt_omploop_sfence(); // ensure that nontemporal writes complete before we attempt to read output
524 }
525 else
526 {
528 for(size_t y = 0; y < stride; y += 4)
529 {
530 const float *const restrict in = __builtin_assume_aligned(image_in + y, 16);
531 float *const restrict out = __builtin_assume_aligned(image_out + y, 16);
532
533 const dt_aligned_pixel_simd_t vin = dt_load_simd_aligned(in);
534 dt_store_simd_nontemporal(out, dt_mat3x4_mul_vec4(vin, m0, m1, m2));
535 }
536 dt_omploop_sfence(); // ensure that nontemporal writes complete before we attempt to read output
537 }
538}
539
540
541static inline void _transform_matrix(struct dt_iop_module_t *self,
542 const float *const restrict image_in,
543 float *const restrict image_out,
544 const int width, const int height,
545 const dt_iop_colorspace_type_t cst_from,
546 const dt_iop_colorspace_type_t cst_to,
547 dt_iop_colorspace_type_t *converted_cst,
548 const dt_iop_order_iccprofile_info_t *const profile_info)
549{
550 if(cst_from == cst_to)
551 {
552 *converted_cst = cst_to;
553 return;
554 }
555
556 *converted_cst = cst_to;
557
558 if(dt_iop_colorspace_is_rgb(cst_from) && cst_to == IOP_CS_LAB)
559 {
560 _transform_rgb_to_lab_matrix(image_in, image_out, width, height, profile_info);
561 }
562 else if(cst_from == IOP_CS_LAB && dt_iop_colorspace_is_rgb(cst_to))
563 {
564 _transform_lab_to_rgb_matrix(image_in, image_out, width, height, profile_info);
565 }
566 else
567 {
568 *converted_cst = cst_from;
569 fprintf(stderr, "[_transform_matrix] invalid conversion from %i to %i\n", cst_from, cst_to);
570 }
571}
572
573
574#define DT_IOPPR_LUT_SAMPLES 0x10000
575
578{
579 profile_info->type = DT_COLORSPACE_NONE;
580 profile_info->filename[0] = '\0';
581 profile_info->intent = DT_INTENT_PERCEPTUAL;
582 _mark_as_nonmatrix_profile(profile_info);
583 profile_info->unbounded_coeffs_in[0][0] = profile_info->unbounded_coeffs_in[1][0] = profile_info->unbounded_coeffs_in[2][0] = -1.0f;
584 profile_info->unbounded_coeffs_out[0][0] = profile_info->unbounded_coeffs_out[1][0] = profile_info->unbounded_coeffs_out[2][0] = -1.0f;
585 profile_info->nonlinearlut = 0;
586 profile_info->grey = 0.f;
587 profile_info->lutsize = (lutsize > 0) ? lutsize: DT_IOPPR_LUT_SAMPLES;
588 for(int i = 0; i < 3; i++)
589 {
590 profile_info->lut_in[i] = dt_alloc_align_float(profile_info->lutsize);
591 profile_info->lut_in[i][0] = -1.0f;
592 profile_info->lut_out[i] = dt_alloc_align_float(profile_info->lutsize);
593 profile_info->lut_out[i][0] = -1.0f;
594 }
595}
596
597#undef DT_IOPPR_LUT_SAMPLES
598
600{
601 for(int i = 0; i < 3; i++)
602 {
603 dt_free_align(profile_info->lut_in[i]);
604 profile_info->lut_in[i] = NULL;
605 dt_free_align(profile_info->lut_out[i]);
606 profile_info->lut_out[i] = NULL;
607 }
608}
609
615static int dt_ioppr_generate_profile_info(dt_iop_order_iccprofile_info_t *profile_info, const int type, const char *filename, const int intent)
616{
617 int err_code = 0;
618 cmsHPROFILE *rgb_profile = NULL;
619
620 _mark_as_nonmatrix_profile(profile_info);
621 _clear_lut_curves(profile_info);
622
623 profile_info->nonlinearlut = 0;
624 profile_info->grey = 0.1842f;
625
626 profile_info->type = type;
627 g_strlcpy(profile_info->filename, filename, sizeof(profile_info->filename));
628 profile_info->intent = intent;
629
631 pthread_rwlock_rdlock(&darktable.color_profiles->xprofile_lock);
632
633 const dt_colorspaces_color_profile_t *profile
635 if(profile) rgb_profile = profile->profile;
636
638 pthread_rwlock_unlock(&darktable.color_profiles->xprofile_lock);
639
640 // we only allow rgb profiles
641 if(rgb_profile)
642 {
643 cmsColorSpaceSignature rgb_color_space = cmsGetColorSpace(rgb_profile);
644 if(rgb_color_space != cmsSigRgbData)
645 {
646 fprintf(stderr, "working profile color space `%c%c%c%c' not supported\n",
647 (char)(rgb_color_space>>24),
648 (char)(rgb_color_space>>16),
649 (char)(rgb_color_space>>8),
650 (char)(rgb_color_space));
651 rgb_profile = NULL;
652 }
653 }
654
655 // get the matrix
656 if(rgb_profile)
657 {
658 if(dt_colorspaces_get_matrix_from_input_profile(rgb_profile, profile_info->matrix_in, profile_info->lut_in[0],
659 profile_info->lut_in[1], profile_info->lut_in[2],
660 profile_info->lutsize)
661 || dt_colorspaces_get_matrix_from_output_profile(rgb_profile, profile_info->matrix_out,
662 profile_info->lut_out[0], profile_info->lut_out[1],
663 profile_info->lut_out[2], profile_info->lutsize))
664 {
665 _mark_as_nonmatrix_profile(profile_info);
666 _clear_lut_curves(profile_info);
667 }
668 else if(isnan(profile_info->matrix_in[0][0]) || isnan(profile_info->matrix_out[0][0]))
669 {
670 _mark_as_nonmatrix_profile(profile_info);
671 _clear_lut_curves(profile_info);
672 }
673 else
674 {
675 transpose_3xSSE(profile_info->matrix_in, profile_info->matrix_in_transposed);
676 transpose_3xSSE(profile_info->matrix_out, profile_info->matrix_out_transposed);
677 }
678 }
679
680 // now try to initialize unbounded mode:
681 // we do extrapolation for input values above 1.0f.
682 // unfortunately we can only do this if we got the computation
683 // in our hands, i.e. for the fast builtin-dt-matrix-profile path.
684 if(!isnan(profile_info->matrix_in[0][0]) && !isnan(profile_info->matrix_out[0][0]))
685 {
686 profile_info->nonlinearlut = _init_unbounded_coeffs(profile_info->lut_in[0], profile_info->lut_in[1], profile_info->lut_in[2],
687 profile_info->unbounded_coeffs_in[0], profile_info->unbounded_coeffs_in[1], profile_info->unbounded_coeffs_in[2], profile_info->lutsize);
688 _init_unbounded_coeffs(profile_info->lut_out[0], profile_info->lut_out[1], profile_info->lut_out[2],
689 profile_info->unbounded_coeffs_out[0], profile_info->unbounded_coeffs_out[1], profile_info->unbounded_coeffs_out[2], profile_info->lutsize);
690 }
691
692 if(!isnan(profile_info->matrix_in[0][0]) && !isnan(profile_info->matrix_out[0][0]) && profile_info->nonlinearlut)
693 {
694 const dt_aligned_pixel_t rgb = { 0.1842f, 0.1842f, 0.1842f };
695 profile_info->grey = dt_ioppr_get_rgb_matrix_luminance(rgb, profile_info->matrix_in, profile_info->lut_in, profile_info->unbounded_coeffs_in, profile_info->lutsize, profile_info->nonlinearlut);
696 }
697
698 return err_code;
699}
700
704 const dt_colorspaces_color_profile_type_t profile_type,
705 const char *profile_filename)
706{
707 dt_iop_order_iccprofile_info_t *profile_info = NULL;
708
709 for(GList *profiles = dev->allprofile_info; profiles; profiles = g_list_next(profiles))
710 {
712 if(prof->type == profile_type && strcmp(prof->filename, profile_filename) == 0)
713 {
714 profile_info = prof;
715 break;
716 }
717 }
718
719 return profile_info;
720}
721
724 const dt_colorspaces_color_profile_type_t profile_type,
725 const char *profile_filename,
726 const int intent)
727{
728 dt_iop_order_iccprofile_info_t *profile_info = dt_ioppr_get_profile_info_from_list(dev, profile_type, profile_filename);
729 if(IS_NULL_PTR(profile_info))
730 {
731 profile_info = dt_alloc_align(sizeof(dt_iop_order_iccprofile_info_t));
732 dt_ioppr_init_profile_info(profile_info, 0);
733 const int err = dt_ioppr_generate_profile_info(profile_info, profile_type, profile_filename, intent);
734 if(err == 0)
735 {
736 dev->allprofile_info = g_list_append(dev->allprofile_info, profile_info);
737 }
738 else
739 {
740 dt_free_align(profile_info);
741 profile_info = NULL;
742 }
743 }
744 return profile_info;
745}
746
748{
749 dt_iop_order_iccprofile_info_t *profile = NULL;
750
751 // first check if the module is between colorin and colorout
752 gboolean in_between = FALSE;
753
754 for(GList *modules = iop_list; modules; modules = g_list_next(modules))
755 {
756 dt_iop_module_t *mod = (dt_iop_module_t *)(modules->data);
757
758 // we reach the module, that's it
759 if(strcmp(mod->op, module->op) == 0) break;
760
761 // if we reach colorout means that the module is after it
762 if(strcmp(mod->op, "colorout") == 0)
763 {
764 in_between = FALSE;
765 break;
766 }
767
768 // we reach colorin, so far we're good
769 if(strcmp(mod->op, "colorin") == 0)
770 {
771 in_between = TRUE;
772 break;
773 }
774 }
775
776 if(in_between)
777 {
779 const char *filename = NULL;
780 dt_develop_t *dev = module->dev;
781
782 dt_ioppr_get_work_profile_type(dev, &type, &filename);
783 if(filename) profile = dt_ioppr_add_profile_info_to_list(dev, type, filename, DT_INTENT_PERCEPTUAL);
784 }
785
786 return profile;
787}
788
791 struct dt_dev_pixelpipe_t *pipe,
793 const char *filename,
794 const int intent)
795{
796 dt_iop_order_iccprofile_info_t *profile_info = dt_ioppr_add_profile_info_to_list(dev, type, filename, intent);
797
798 if(IS_NULL_PTR(profile_info) || isnan(profile_info->matrix_in[0][0]) || isnan(profile_info->matrix_out[0][0]))
799 {
800 fprintf(stderr, "[dt_ioppr_set_pipe_work_profile_info] unsupported working profile %i %s, it will be replaced with linear Rec2020\n", type, filename);
801 profile_info = dt_ioppr_add_profile_info_to_list(dev, DT_COLORSPACE_LIN_REC2020, "", intent);
802 }
803 pipe->work_profile_info = profile_info;
804
805 return profile_info;
806}
807
810 struct dt_dev_pixelpipe_t *pipe,
812 const char *filename,
813 const int intent,
814 const dt_colormatrix_t matrix_in)
815{
816 dt_iop_order_iccprofile_info_t *profile_info = dt_ioppr_add_profile_info_to_list(dev, type, filename, intent);
817
818 if(IS_NULL_PTR(profile_info))
819 {
820 fprintf(stderr,
821 "[dt_ioppr_set_pipe_input_profile_info] unsupported input profile %i %s, it will be replaced with "
822 "linear Rec2020\n",
823 type, filename);
824 profile_info = dt_ioppr_add_profile_info_to_list(dev, DT_COLORSPACE_LIN_REC2020, "", intent);
825 }
826
827 if(profile_info->type >= DT_COLORSPACE_EMBEDDED_ICC && profile_info->type <= DT_COLORSPACE_ALTERNATE_MATRIX)
828 {
829 /* We have a camera input matrix, these are not generated from files but in colorin,
830 * so we need to fetch and replace them from somewhere.
831 */
832 memcpy(profile_info->matrix_in, matrix_in, sizeof(profile_info->matrix_in));
833 mat3SSEinv(profile_info->matrix_out, profile_info->matrix_in);
834 transpose_3xSSE(profile_info->matrix_in, profile_info->matrix_in_transposed);
835 transpose_3xSSE(profile_info->matrix_out, profile_info->matrix_out_transposed);
836 }
837 pipe->input_profile_info = profile_info;
838
839 return profile_info;
840}
841
844 struct dt_dev_pixelpipe_t *pipe,
846 const char *filename,
847 const int intent)
848{
849 dt_iop_order_iccprofile_info_t *profile_info = dt_ioppr_add_profile_info_to_list(dev, type, filename, intent);
850
851 if(IS_NULL_PTR(profile_info) || isnan(profile_info->matrix_in[0][0]) || isnan(profile_info->matrix_out[0][0]))
852 {
854 {
855 // ??? this error output has been disabled for a display profile.
856 // see discussion in https://github.com/darktable-org/darktable/issues/6774
857 fprintf(stderr,
858 "[dt_ioppr_set_pipe_output_profile_info] unsupported output"
859 " profile %i %s, it will be replaced with sRGB\n",
860 type, filename);
861 }
862 profile_info = dt_ioppr_add_profile_info_to_list(dev, DT_COLORSPACE_SRGB, "", intent);
863 }
864 pipe->output_profile_info = profile_info;
865
866 return profile_info;
867}
868
873
878
883
885 const struct dt_dev_pixelpipe_t *pipe)
886{
887 dt_iop_order_iccprofile_info_t *restrict color_profile;
888
889 const int colorin_order = dt_ioppr_get_iop_order(module->dev->iop_order_list, "colorin", 0);
890 const int colorout_order = dt_ioppr_get_iop_order(module->dev->iop_order_list, "colorout", 0);
891 const int current_module_order = module->iop_order;
892
893 if(current_module_order < colorin_order)
894 color_profile = dt_ioppr_get_pipe_input_profile_info(pipe);
895 else if(current_module_order < colorout_order)
896 color_profile = dt_ioppr_get_pipe_work_profile_info(pipe);
897 else
898 color_profile = dt_ioppr_get_pipe_output_profile_info(pipe);
899
900 return color_profile;
901}
902
903// returns a pointer to the filename of the work profile instead of the actual string data
904// pointer must not be stored
907 const char **profile_filename)
908{
909 *profile_type = DT_COLORSPACE_NONE;
910 *profile_filename = NULL;
911
912 // use introspection to get the params values
913 dt_iop_module_so_t *colorin_so = NULL;
914 dt_iop_module_t *colorin = NULL;
915 for(const GList *modules = darktable.iop; modules; modules = g_list_next(modules))
916 {
917 dt_iop_module_so_t *module_so = (dt_iop_module_so_t *)(modules->data);
918 if(!strcmp(module_so->op, "colorin"))
919 {
920 colorin_so = module_so;
921 break;
922 }
923 }
924 if(colorin_so && colorin_so->get_p)
925 {
926 for(const GList *modules = dev->iop; modules; modules = g_list_next(modules))
927 {
928 dt_iop_module_t *module = (dt_iop_module_t *)(modules->data);
929 if(!strcmp(module->op, "colorin"))
930 {
931 colorin = module;
932 break;
933 }
934 }
935 }
936 if(colorin)
937 {
938 dt_colorspaces_color_profile_type_t *_type = colorin_so->get_p(colorin->params, "type_work");
939 char *_filename = colorin_so->get_p(colorin->params, "filename_work");
940 if(_type && _filename)
941 {
942 *profile_type = *_type;
943 *profile_filename = _filename;
944 }
945 else
946 fprintf(stderr, "[dt_ioppr_get_work_profile_type] can't get colorin parameters\n");
947 }
948 else
949 fprintf(stderr, "[dt_ioppr_get_work_profile_type] can't find colorin iop\n");
950}
951
954 const char **profile_filename)
955{
956 *profile_type = DT_COLORSPACE_NONE;
957 *profile_filename = NULL;
958
959 // use introspection to get the params values
960 dt_iop_module_so_t *colorout_so = NULL;
961 dt_iop_module_t *colorout = NULL;
962 for(const GList *modules = g_list_last(darktable.iop); modules; modules = g_list_previous(modules))
963 {
964 dt_iop_module_so_t *module_so = (dt_iop_module_so_t *)(modules->data);
965 if(!strcmp(module_so->op, "colorout"))
966 {
967 colorout_so = module_so;
968 break;
969 }
970 }
971 if(colorout_so && colorout_so->get_p)
972 {
973 for(const GList *modules = g_list_last(dev->iop); modules; modules = g_list_previous(modules))
974 {
975 dt_iop_module_t *module = (dt_iop_module_t *)(modules->data);
976 if(!strcmp(module->op, "colorout"))
977 {
978 colorout = module;
979 break;
980 }
981 }
982 }
983 if(colorout)
984 {
985 dt_colorspaces_color_profile_type_t *_type = colorout_so->get_p(colorout->params, "type");
986 char *_filename = colorout_so->get_p(colorout->params, "filename");
987 if(_type && _filename)
988 {
989 *profile_type = *_type;
990 *profile_filename = _filename;
991 }
992 else
993 fprintf(stderr, "[dt_ioppr_get_export_profile_type] can't get colorout parameters\n");
994 }
995 else
996 fprintf(stderr, "[dt_ioppr_get_export_profile_type] can't find colorout iop\n");
997}
998
999void dt_ioppr_transform_image_colorspace(struct dt_iop_module_t *self, const float *const image_in,
1000 float *const image_out, const int width, const int height,
1001 const int cst_from, const int cst_to, int *converted_cst,
1002 const dt_iop_order_iccprofile_info_t *const profile_info)
1003{
1004 if(cst_from == cst_to)
1005 {
1006 *converted_cst = cst_to;
1007 return;
1008 }
1009 if(dt_iop_colorspace_is_rgb(cst_from) && dt_iop_colorspace_is_rgb(cst_to))
1010 {
1011 *converted_cst = cst_to;
1012 return;
1013 }
1014 if(IS_NULL_PTR(profile_info))
1015 {
1016 *converted_cst = cst_from;
1017 return;
1018 }
1019 if(profile_info->type == DT_COLORSPACE_NONE)
1020 {
1021 *converted_cst = cst_from;
1022 return;
1023 }
1024
1025 dt_times_t start_time = { 0 }, end_time = { 0 };
1026 if(darktable.unmuted & DT_DEBUG_PERF) dt_get_times(&start_time);
1027
1028 // matrix should be never NAN, this is only to test it against lcms2!
1029 if(!isnan(profile_info->matrix_in[0][0]) && !isnan(profile_info->matrix_out[0][0]))
1030 {
1031 _transform_matrix(self, image_in, image_out, width, height, cst_from, cst_to, converted_cst, profile_info);
1032
1034 {
1035 dt_get_times(&end_time);
1036 fprintf(stderr, "image colorspace transform %s-->%s took %.3f secs (%.3f CPU) [%s %s]\n",
1037 dt_iop_colorspace_is_rgb(cst_from) ? "RGB" : "Lab",
1038 dt_iop_colorspace_is_rgb(cst_to) ? "RGB" : "Lab",
1039 end_time.clock - start_time.clock, end_time.user - start_time.user, self->op, self->multi_name);
1040 }
1041 }
1042 else
1043 {
1044 _transform_lcms2(self, image_in, image_out, width, height, cst_from, cst_to, converted_cst, profile_info);
1045
1047 {
1048 dt_get_times(&end_time);
1049 fprintf(stderr, "image colorspace transform %s-->%s took %.3f secs (%.3f lcms2) [%s %s]\n",
1050 dt_iop_colorspace_is_rgb(cst_from) ? "RGB" : "Lab",
1051 dt_iop_colorspace_is_rgb(cst_to) ? "RGB" : "Lab",
1052 end_time.clock - start_time.clock, end_time.user - start_time.user, self->op, self->multi_name);
1053 }
1054 }
1055
1056 if(*converted_cst == cst_from)
1057 fprintf(stderr, "[dt_ioppr_transform_image_colorspace] invalid conversion from %i to %i\n", cst_from, cst_to);
1058}
1059
1060
1061void dt_ioppr_transform_image_colorspace_rgb(const float *const restrict image_in, float *const restrict image_out, const int width,
1062 const int height,
1063 const dt_iop_order_iccprofile_info_t *const profile_info_from,
1064 const dt_iop_order_iccprofile_info_t *const profile_info_to,
1065 const char *message)
1066{
1067 if(profile_info_from->type == DT_COLORSPACE_NONE || profile_info_to->type == DT_COLORSPACE_NONE)
1068 {
1069 return;
1070 }
1071 if(profile_info_from->type == profile_info_to->type
1072 && strcmp(profile_info_from->filename, profile_info_to->filename) == 0)
1073 {
1074 if(image_in != image_out)
1075 memcpy(image_out, image_in, sizeof(float) * 4 * width * height);
1076
1077 return;
1078 }
1079
1080 dt_times_t start_time = { 0 }, end_time = { 0 };
1081 if(darktable.unmuted & DT_DEBUG_PERF) dt_get_times(&start_time);
1082
1083 if(!isnan(profile_info_from->matrix_in[0][0]) && !isnan(profile_info_from->matrix_out[0][0])
1084 && !isnan(profile_info_to->matrix_in[0][0]) && !isnan(profile_info_to->matrix_out[0][0]))
1085 {
1086 _transform_matrix_rgb(image_in, image_out, width, height, profile_info_from, profile_info_to);
1087
1089 {
1090 dt_get_times(&end_time);
1091 fprintf(stderr, "image colorspace transform RGB-->RGB took %.3f secs (%.3f CPU) [%s]\n",
1092 end_time.clock - start_time.clock, end_time.user - start_time.user, (message) ? message : "");
1093 }
1094 }
1095 else
1096 {
1097 _transform_lcms2_rgb(image_in, image_out, width, height, profile_info_from, profile_info_to);
1098
1100 {
1101 dt_get_times(&end_time);
1102 fprintf(stderr, "image colorspace transform RGB-->RGB took %.3f secs (%.3f lcms2) [%s]\n",
1103 end_time.clock - start_time.clock, end_time.user - start_time.user, (message) ? message : "");
1104 }
1105 }
1106}
1107
1108#ifdef HAVE_OPENCL
1110{
1112
1113 const int program = 23; // colorspaces.cl, from programs.conf
1114 g->kernel_colorspaces_transform_lab_to_rgb_matrix = dt_opencl_create_kernel(program, "colorspaces_transform_lab_to_rgb_matrix");
1115 g->kernel_colorspaces_transform_rgb_matrix_to_lab = dt_opencl_create_kernel(program, "colorspaces_transform_rgb_matrix_to_lab");
1116 g->kernel_colorspaces_transform_rgb_matrix_to_rgb
1117 = dt_opencl_create_kernel(program, "colorspaces_transform_rgb_matrix_to_rgb");
1118 return g;
1119}
1120
1122{
1123 if(IS_NULL_PTR(g)) return;
1124
1125 // destroy kernels
1126 dt_opencl_free_kernel(g->kernel_colorspaces_transform_lab_to_rgb_matrix);
1127 dt_opencl_free_kernel(g->kernel_colorspaces_transform_rgb_matrix_to_lab);
1128 dt_opencl_free_kernel(g->kernel_colorspaces_transform_rgb_matrix_to_rgb);
1129
1130 dt_free(g);
1131}
1132
1134{
1135 for(int i = 0; i < 9; i++)
1136 {
1137 profile_info_cl->matrix_in[i] = profile_info->matrix_in[i/3][i%3];
1138 profile_info_cl->matrix_out[i] = profile_info->matrix_out[i/3][i%3];
1139 }
1140 profile_info_cl->lutsize = profile_info->lutsize;
1141 for(int i = 0; i < 3; i++)
1142 {
1143 for(int j = 0; j < 3; j++)
1144 {
1145 profile_info_cl->unbounded_coeffs_in[i][j] = profile_info->unbounded_coeffs_in[i][j];
1146 profile_info_cl->unbounded_coeffs_out[i][j] = profile_info->unbounded_coeffs_out[i][j];
1147 }
1148 }
1149 profile_info_cl->nonlinearlut = profile_info->nonlinearlut;
1150 profile_info_cl->grey = profile_info->grey;
1151}
1152
1153cl_float *dt_ioppr_get_trc_cl(const dt_iop_order_iccprofile_info_t *const profile_info)
1154{
1155 cl_float *trc = malloc(sizeof(cl_float) * 6 * profile_info->lutsize);
1156 if(trc)
1157 {
1158 int x = 0;
1159 for(int c = 0; c < 3; c++)
1160 for(int y = 0; y < profile_info->lutsize; y++, x++)
1161 trc[x] = profile_info->lut_in[c][y];
1162 for(int c = 0; c < 3; c++)
1163 for(int y = 0; y < profile_info->lutsize; y++, x++)
1164 trc[x] = profile_info->lut_out[c][y];
1165 }
1166 return trc;
1167}
1168
1170 const int devid, dt_colorspaces_iccprofile_info_cl_t **_profile_info_cl,
1171 cl_float **_profile_lut_cl, cl_mem *_dev_profile_info,
1172 cl_mem *_dev_profile_lut)
1173{
1174 cl_int err = CL_SUCCESS;
1175
1177 cl_float *profile_lut_cl = NULL;
1178 cl_mem dev_profile_info = NULL;
1179 cl_mem dev_profile_lut = NULL;
1180
1181 if(profile_info)
1182 {
1183 dt_ioppr_get_profile_info_cl(profile_info, profile_info_cl);
1184 profile_lut_cl = dt_ioppr_get_trc_cl(profile_info);
1185
1186 dev_profile_info = dt_opencl_copy_host_to_device_constant(devid, sizeof(*profile_info_cl), profile_info_cl);
1187 if(IS_NULL_PTR(dev_profile_info))
1188 {
1189 fprintf(stderr, "[dt_ioppr_build_iccprofile_params_cl] error allocating memory 5\n");
1190 err = CL_MEM_OBJECT_ALLOCATION_FAILURE;
1191 goto cleanup;
1192 }
1193
1194 dev_profile_lut = dt_opencl_copy_host_to_device(devid, profile_lut_cl, 256, 256 * 6, sizeof(float));
1195 if(IS_NULL_PTR(dev_profile_lut))
1196 {
1197 fprintf(stderr, "[dt_ioppr_build_iccprofile_params_cl] error allocating memory 6\n");
1198 err = CL_MEM_OBJECT_ALLOCATION_FAILURE;
1199 goto cleanup;
1200 }
1201 }
1202 else
1203 {
1204 profile_lut_cl = malloc(sizeof(cl_float) * 1 * 6);
1205
1206 dev_profile_lut = dt_opencl_copy_host_to_device(devid, profile_lut_cl, 1, 1 * 6, sizeof(float));
1207 if(IS_NULL_PTR(dev_profile_lut))
1208 {
1209 fprintf(stderr, "[dt_ioppr_build_iccprofile_params_cl] error allocating memory 7\n");
1210 err = CL_MEM_OBJECT_ALLOCATION_FAILURE;
1211 goto cleanup;
1212 }
1213 }
1214
1215cleanup:
1216 *_profile_info_cl = profile_info_cl;
1217 *_profile_lut_cl = profile_lut_cl;
1218 *_dev_profile_info = dev_profile_info;
1219 *_dev_profile_lut = dev_profile_lut;
1220
1221 return err;
1222}
1223
1225 cl_float **_profile_lut_cl, cl_mem *_dev_profile_info,
1226 cl_mem *_dev_profile_lut)
1227{
1228 dt_colorspaces_iccprofile_info_cl_t *profile_info_cl = *_profile_info_cl;
1229 cl_float *profile_lut_cl = *_profile_lut_cl;
1230 cl_mem dev_profile_info = *_dev_profile_info;
1231 cl_mem dev_profile_lut = *_dev_profile_lut;
1232
1233 if(profile_info_cl)
1234 {
1235 dt_free(profile_info_cl);
1236 }
1237 dt_opencl_release_mem_object(dev_profile_info);
1238 dt_opencl_release_mem_object(dev_profile_lut);
1239 dt_free(profile_lut_cl);
1240
1241 *_profile_info_cl = NULL;
1242 *_profile_lut_cl = NULL;
1243 *_dev_profile_info = NULL;
1244 *_dev_profile_lut = NULL;
1245}
1246
1247int dt_ioppr_transform_image_colorspace_cl(struct dt_iop_module_t *self, const int devid, cl_mem dev_img_in,
1248 cl_mem dev_img_out, const int width, const int height,
1249 const int cst_from, const int cst_to, int *converted_cst,
1250 const dt_iop_order_iccprofile_info_t *const profile_info)
1251{
1252 cl_int err = CL_SUCCESS;
1253
1254 assert(!IS_NULL_PTR(dev_img_in));
1255 assert(!IS_NULL_PTR(dev_img_out));
1256 assert(dev_img_in != dev_img_out);
1257
1258 if(cst_from == cst_to)
1259 {
1260 *converted_cst = cst_to;
1261 return TRUE;
1262 }
1263 if(dt_iop_colorspace_is_rgb(cst_from) && dt_iop_colorspace_is_rgb(cst_to))
1264 {
1265 *converted_cst = cst_to;
1266 return TRUE;
1267 }
1268 if(IS_NULL_PTR(profile_info))
1269 {
1270 *converted_cst = cst_from;
1271 return FALSE;
1272 }
1273 if(profile_info->type == DT_COLORSPACE_NONE)
1274 {
1275 *converted_cst = cst_from;
1276 return FALSE;
1277 }
1278
1279 const size_t ch = 4;
1280 float *src_buffer = NULL;
1281
1282 int kernel_transform = 0;
1283 cl_mem dev_profile_info = NULL;
1284 cl_mem dev_lut = NULL;
1286 cl_float *lut_cl = NULL;
1287
1288 *converted_cst = cst_from;
1289
1290 // if we have a matrix use opencl
1291 if(!isnan(profile_info->matrix_in[0][0]) && !isnan(profile_info->matrix_out[0][0]))
1292 {
1293 dt_times_t start_time = { 0 }, end_time = { 0 };
1294 if(darktable.unmuted & DT_DEBUG_PERF) dt_get_times(&start_time);
1295
1296 if(dt_iop_colorspace_is_rgb(cst_from) && cst_to == IOP_CS_LAB)
1297 {
1299 }
1300 else if(cst_from == IOP_CS_LAB && dt_iop_colorspace_is_rgb(cst_to))
1301 {
1303 }
1304 else
1305 {
1306 err = CL_INVALID_KERNEL;
1307 *converted_cst = cst_from;
1308 fprintf(stderr, "[dt_ioppr_transform_image_colorspace_cl] invalid conversion from %i to %i\n", cst_from, cst_to);
1309 goto cleanup;
1310 }
1311
1312 dt_ioppr_get_profile_info_cl(profile_info, &profile_info_cl);
1313 lut_cl = dt_ioppr_get_trc_cl(profile_info);
1314
1315 dev_profile_info = dt_opencl_copy_host_to_device_constant(devid, sizeof(profile_info_cl), &profile_info_cl);
1316 if(IS_NULL_PTR(dev_profile_info))
1317 {
1318 fprintf(stderr, "[dt_ioppr_transform_image_colorspace_cl] error allocating memory for color transformation 5\n");
1319 err = CL_MEM_OBJECT_ALLOCATION_FAILURE;
1320 goto cleanup;
1321 }
1322 dev_lut = dt_opencl_copy_host_to_device(devid, lut_cl, 256, 256 * 6, sizeof(float));
1323 if(IS_NULL_PTR(dev_lut))
1324 {
1325 fprintf(stderr, "[dt_ioppr_transform_image_colorspace_cl] error allocating memory for color transformation 6\n");
1326 err = CL_MEM_OBJECT_ALLOCATION_FAILURE;
1327 goto cleanup;
1328 }
1329
1330 size_t sizes[] = { ROUNDUPDWD(width, devid), ROUNDUPDHT(height, devid), 1 };
1331
1332 dt_opencl_set_kernel_arg(devid, kernel_transform, 0, sizeof(cl_mem), (void *)&dev_img_in);
1333 dt_opencl_set_kernel_arg(devid, kernel_transform, 1, sizeof(cl_mem), (void *)&dev_img_out);
1334 dt_opencl_set_kernel_arg(devid, kernel_transform, 2, sizeof(int), (void *)&width);
1335 dt_opencl_set_kernel_arg(devid, kernel_transform, 3, sizeof(int), (void *)&height);
1336 dt_opencl_set_kernel_arg(devid, kernel_transform, 4, sizeof(cl_mem), (void *)&dev_profile_info);
1337 dt_opencl_set_kernel_arg(devid, kernel_transform, 5, sizeof(cl_mem), (void *)&dev_lut);
1338 err = dt_opencl_enqueue_kernel_2d(devid, kernel_transform, sizes);
1339 if(err != CL_SUCCESS)
1340 {
1341 fprintf(stderr, "[dt_ioppr_transform_image_colorspace_cl] error %i enqueue kernel for color transformation\n", err);
1342 goto cleanup;
1343 }
1344
1345 *converted_cst = cst_to;
1346
1348 {
1349 dt_get_times(&end_time);
1350 fprintf(stderr, "image colorspace transform %s-->%s took %.3f secs (%.3f GPU) [%s %s]\n",
1351 dt_iop_colorspace_is_rgb(cst_from) ? "RGB" : "Lab",
1352 dt_iop_colorspace_is_rgb(cst_to) ? "RGB" : "Lab",
1353 end_time.clock - start_time.clock, end_time.user - start_time.user, self->op, self->multi_name);
1354 }
1355 }
1356 else
1357 {
1358 // no matrix, call lcms2
1360 if(IS_NULL_PTR(src_buffer))
1361 {
1362 fprintf(stderr, "[dt_ioppr_transform_image_colorspace_cl] error allocating memory for color transformation 1\n");
1363 err = CL_MEM_OBJECT_ALLOCATION_FAILURE;
1364 goto cleanup;
1365 }
1366
1367 err = dt_opencl_copy_device_to_host(devid, src_buffer, dev_img_in, width, height, ch * sizeof(float));
1368 if(err != CL_SUCCESS)
1369 {
1370 fprintf(stderr, "[dt_ioppr_transform_image_colorspace_cl] error allocating memory for color transformation 2\n");
1371 goto cleanup;
1372 }
1373
1374 // just call the CPU version for now
1375 dt_ioppr_transform_image_colorspace(self, src_buffer, src_buffer, width, height, cst_from, cst_to,
1376 converted_cst, profile_info);
1377
1378 err = dt_opencl_write_host_to_device(devid, src_buffer, dev_img_out, width, height, ch * sizeof(float));
1379 if(err != CL_SUCCESS)
1380 {
1381 fprintf(stderr, "[dt_ioppr_transform_image_colorspace_cl] error allocating memory for color transformation 3\n");
1382 goto cleanup;
1383 }
1384 }
1385
1386cleanup:
1388 dt_opencl_release_mem_object(dev_profile_info);
1390 dt_free(lut_cl);
1391
1392 return (err == CL_SUCCESS) ? TRUE : FALSE;
1393}
1394
1395int dt_ioppr_transform_image_colorspace_rgb_cl(const int devid, cl_mem dev_img_in, cl_mem dev_img_out,
1396 const int width, const int height,
1397 const dt_iop_order_iccprofile_info_t *const profile_info_from,
1398 const dt_iop_order_iccprofile_info_t *const profile_info_to,
1399 const char *message)
1400{
1401 cl_int err = CL_SUCCESS;
1402
1403 if(profile_info_from->type == DT_COLORSPACE_NONE || profile_info_to->type == DT_COLORSPACE_NONE)
1404 {
1405 return FALSE;
1406 }
1407 if(profile_info_from->type == profile_info_to->type
1408 && strcmp(profile_info_from->filename, profile_info_to->filename) == 0)
1409 {
1410 if(dev_img_in != dev_img_out)
1411 {
1412 size_t origin[] = { 0, 0, 0 };
1413 size_t region[] = { width, height, 1 };
1414
1415 err = dt_opencl_enqueue_copy_image(devid, dev_img_in, dev_img_out, origin, origin, region);
1416 if(err != CL_SUCCESS)
1417 {
1418 fprintf(stderr,
1419 "[dt_ioppr_transform_image_colorspace_rgb_cl] error on copy image for color transformation\n");
1420 return FALSE;
1421 }
1422 }
1423
1424 return TRUE;
1425 }
1426
1427 const size_t ch = 4;
1428 float *src_buffer_in = NULL;
1429 float *src_buffer_out = NULL;
1430 int in_place = (dev_img_in == dev_img_out);
1431
1432 int kernel_transform = 0;
1433 cl_mem dev_tmp = NULL;
1434
1435 cl_mem dev_profile_info_from = NULL;
1436 cl_mem dev_lut_from = NULL;
1437 dt_colorspaces_iccprofile_info_cl_t profile_info_from_cl;
1438 cl_float *lut_from_cl = NULL;
1439
1440 cl_mem dev_profile_info_to = NULL;
1441 cl_mem dev_lut_to = NULL;
1442 dt_colorspaces_iccprofile_info_cl_t profile_info_to_cl;
1443 cl_float *lut_to_cl = NULL;
1444
1445 cl_mem matrix_cl = NULL;
1446
1447 // if we have a matrix use opencl
1448 if(!isnan(profile_info_from->matrix_in[0][0]) && !isnan(profile_info_from->matrix_out[0][0])
1449 && !isnan(profile_info_to->matrix_in[0][0]) && !isnan(profile_info_to->matrix_out[0][0]))
1450 {
1451 dt_times_t start_time = { 0 }, end_time = { 0 };
1452 if(darktable.unmuted & DT_DEBUG_PERF) dt_get_times(&start_time);
1453
1454 size_t origin[] = { 0, 0, 0 };
1455 size_t region[] = { width, height, 1 };
1456
1458
1459 dt_ioppr_get_profile_info_cl(profile_info_from, &profile_info_from_cl);
1460 lut_from_cl = dt_ioppr_get_trc_cl(profile_info_from);
1461
1462 dt_ioppr_get_profile_info_cl(profile_info_to, &profile_info_to_cl);
1463 lut_to_cl = dt_ioppr_get_trc_cl(profile_info_to);
1464
1466 dt_colormatrix_mul(matrix, profile_info_to->matrix_out, profile_info_from->matrix_in);
1467
1468 if(in_place)
1469 {
1470 dev_tmp = dt_opencl_alloc_device(devid, width, height, sizeof(float) * 4);
1471 if(IS_NULL_PTR(dev_tmp))
1472 {
1473 fprintf(
1474 stderr,
1475 "[dt_ioppr_transform_image_colorspace_rgb_cl] error allocating memory for color transformation 4\n");
1476 err = CL_MEM_OBJECT_ALLOCATION_FAILURE;
1477 goto cleanup;
1478 }
1479
1480 err = dt_opencl_enqueue_copy_image(devid, dev_img_in, dev_tmp, origin, origin, region);
1481 if(err != CL_SUCCESS)
1482 {
1483 fprintf(stderr,
1484 "[dt_ioppr_transform_image_colorspace_rgb_cl] error on copy image for color transformation\n");
1485 goto cleanup;
1486 }
1487 }
1488 else
1489 {
1490 dev_tmp = dev_img_in;
1491 }
1492
1493 dev_profile_info_from
1494 = dt_opencl_copy_host_to_device_constant(devid, sizeof(profile_info_from_cl), &profile_info_from_cl);
1495 if(IS_NULL_PTR(dev_profile_info_from))
1496 {
1497 fprintf(stderr,
1498 "[dt_ioppr_transform_image_colorspace_rgb_cl] error allocating memory for color transformation 5\n");
1499 err = CL_MEM_OBJECT_ALLOCATION_FAILURE;
1500 goto cleanup;
1501 }
1502 dev_lut_from = dt_opencl_copy_host_to_device(devid, lut_from_cl, 256, 256 * 6, sizeof(float));
1503 if(IS_NULL_PTR(dev_lut_from))
1504 {
1505 fprintf(stderr,
1506 "[dt_ioppr_transform_image_colorspace_rgb_cl] error allocating memory for color transformation 6\n");
1507 err = CL_MEM_OBJECT_ALLOCATION_FAILURE;
1508 goto cleanup;
1509 }
1510
1511 dev_profile_info_to
1512 = dt_opencl_copy_host_to_device_constant(devid, sizeof(profile_info_to_cl), &profile_info_to_cl);
1513 if(IS_NULL_PTR(dev_profile_info_to))
1514 {
1515 fprintf(stderr,
1516 "[dt_ioppr_transform_image_colorspace_rgb_cl] error allocating memory for color transformation 7\n");
1517 err = CL_MEM_OBJECT_ALLOCATION_FAILURE;
1518 goto cleanup;
1519 }
1520 dev_lut_to = dt_opencl_copy_host_to_device(devid, lut_to_cl, 256, 256 * 6, sizeof(float));
1521 if(IS_NULL_PTR(dev_lut_to))
1522 {
1523 fprintf(stderr,
1524 "[dt_ioppr_transform_image_colorspace_rgb_cl] error allocating memory for color transformation 8\n");
1525 err = CL_MEM_OBJECT_ALLOCATION_FAILURE;
1526 goto cleanup;
1527 }
1528 float matrix3x4[12];
1529 pack_3xSSE_to_3x4(matrix, matrix3x4);
1530 matrix_cl = dt_opencl_copy_host_to_device_constant(devid, sizeof(matrix3x4), matrix3x4);
1531 if(IS_NULL_PTR(matrix_cl))
1532 {
1533 fprintf(stderr,
1534 "[dt_ioppr_transform_image_colorspace_rgb_cl] error allocating memory for color transformation 7\n");
1535 err = CL_MEM_OBJECT_ALLOCATION_FAILURE;
1536 goto cleanup;
1537 }
1538
1539 size_t sizes[] = { ROUNDUPDWD(width, devid), ROUNDUPDHT(height, devid), 1 };
1540
1541 dt_opencl_set_kernel_arg(devid, kernel_transform, 0, sizeof(cl_mem), (void *)&dev_tmp);
1542 dt_opencl_set_kernel_arg(devid, kernel_transform, 1, sizeof(cl_mem), (void *)&dev_img_out);
1543 dt_opencl_set_kernel_arg(devid, kernel_transform, 2, sizeof(int), (void *)&width);
1544 dt_opencl_set_kernel_arg(devid, kernel_transform, 3, sizeof(int), (void *)&height);
1545 dt_opencl_set_kernel_arg(devid, kernel_transform, 4, sizeof(cl_mem), (void *)&dev_profile_info_from);
1546 dt_opencl_set_kernel_arg(devid, kernel_transform, 5, sizeof(cl_mem), (void *)&dev_lut_from);
1547 dt_opencl_set_kernel_arg(devid, kernel_transform, 6, sizeof(cl_mem), (void *)&dev_profile_info_to);
1548 dt_opencl_set_kernel_arg(devid, kernel_transform, 7, sizeof(cl_mem), (void *)&dev_lut_to);
1549 dt_opencl_set_kernel_arg(devid, kernel_transform, 8, sizeof(cl_mem), (void *)&matrix_cl);
1550 err = dt_opencl_enqueue_kernel_2d(devid, kernel_transform, sizes);
1551 if(err != CL_SUCCESS)
1552 {
1553 fprintf(stderr,
1554 "[dt_ioppr_transform_image_colorspace_rgb_cl] error %i enqueue kernel for color transformation\n",
1555 err);
1556 goto cleanup;
1557 }
1558
1560 {
1561 dt_get_times(&end_time);
1562 fprintf(stderr, "image colorspace transform RGB-->RGB took %.3f secs (%.3f GPU) [%s]\n",
1563 end_time.clock - start_time.clock, end_time.user - start_time.user, (message) ? message : "");
1564 }
1565 }
1566 else
1567 {
1568 // no matrix, call lcms2
1571 if(IS_NULL_PTR(src_buffer_in) || IS_NULL_PTR(src_buffer_out))
1572 {
1573 fprintf(stderr,
1574 "[dt_ioppr_transform_image_colorspace_rgb_cl] error allocating memory for color transformation 1\n");
1575 err = CL_MEM_OBJECT_ALLOCATION_FAILURE;
1576 goto cleanup;
1577 }
1578
1579 err = dt_opencl_copy_device_to_host(devid, src_buffer_in, dev_img_in, width, height, ch * sizeof(float));
1580 if(err != CL_SUCCESS)
1581 {
1582 fprintf(stderr,
1583 "[dt_ioppr_transform_image_colorspace_rgb_cl] error allocating memory for color transformation 2\n");
1584 goto cleanup;
1585 }
1586
1587 // just call the CPU version for now
1588 dt_ioppr_transform_image_colorspace_rgb(src_buffer_in, src_buffer_out, width, height, profile_info_from,
1589 profile_info_to, message);
1590
1591 err = dt_opencl_write_host_to_device(devid, src_buffer_out, dev_img_out, width, height, ch * sizeof(float));
1592 if(err != CL_SUCCESS)
1593 {
1594 fprintf(stderr,
1595 "[dt_ioppr_transform_image_colorspace_rgb_cl] error allocating memory for color transformation 3\n");
1596 goto cleanup;
1597 }
1598 }
1599
1600cleanup:
1601 dt_pixelpipe_cache_free_align(src_buffer_in);
1602 dt_pixelpipe_cache_free_align(src_buffer_out);
1603 if(dev_tmp && in_place) dt_opencl_release_mem_object(dev_tmp);
1604
1605 dt_opencl_release_mem_object(dev_profile_info_from);
1606 dt_opencl_release_mem_object(dev_lut_from);
1607 dt_free(lut_from_cl);
1608
1609 dt_opencl_release_mem_object(dev_profile_info_to);
1610 dt_opencl_release_mem_object(dev_lut_to);
1611 dt_free(lut_to_cl);
1612
1614
1615 return (err == CL_SUCCESS) ? TRUE : FALSE;
1616}
1617#endif
1618
1619#undef DT_IOP_ORDER_PROFILE
1620// clang-format off
1621// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
1622// vim: shiftwidth=2 expandtab tabstop=2 cindent
1623// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
1624// clang-format on
#define TRUE
Definition ashift_lsd.c:162
#define FALSE
Definition ashift_lsd.c:158
void cleanup(dt_imageio_module_format_t *self)
Definition avif.c:164
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 basebuffer.c:75
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 basebuffer.c:82
int width
Definition bilateral.h:1
int height
Definition bilateral.h:1
dt_iop_colorspace_type_t
@ IOP_CS_LAB
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)
__DT_CLONE_TARGETS__ void dt_colorspaces_transform_rgba_float_image(const cmsHTRANSFORM transform, const float *image_in, float *image_out, const int width, const int height)
int dt_colorspaces_get_matrix_from_output_profile(cmsHPROFILE prof, dt_colormatrix_t matrix, float *lutr, float *lutg, float *lutb, const int lutsize)
int dt_colorspaces_get_matrix_from_input_profile(cmsHPROFILE prof, dt_colormatrix_t matrix, float *lutr, float *lutg, float *lutb, const int lutsize)
@ DT_INTENT_PERCEPTUAL
Definition colorspaces.h:64
dt_colorspaces_color_profile_type_t
Definition colorspaces.h:81
@ DT_COLORSPACE_EMBEDDED_ICC
Definition colorspaces.h:92
@ 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_ALTERNATE_MATRIX
Definition colorspaces.h:97
@ DT_PROFILE_DIRECTION_WORK
@ DT_PROFILE_DIRECTION_ANY
static dt_aligned_pixel_t rgb
dt_Lab_to_XYZ(Lab, XYZ)
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)))
dt_XYZ_to_Lab(XYZ, Lab)
const dt_colormatrix_t matrix
int type
darktable_t darktable
Definition darktable.c:181
void * dt_alloc_align(size_t size)
Definition darktable.c:446
void dt_print(dt_debug_thread_t thread, const char *msg,...)
Definition darktable.c:1542
#define DT_ALIGNED_PIXEL
Definition darktable.h:389
#define dt_free_align(ptr)
Definition darktable.h:481
@ DT_DEBUG_PERF
Definition darktable.h:719
@ DT_DEBUG_DEV
Definition darktable.h:717
#define for_each_channel(_var,...)
Definition darktable.h:662
#define dt_pixelpipe_cache_alloc_align_float_cache(pixels, id)
Definition darktable.h:447
static float * dt_alloc_align_float(size_t pixels)
Definition darktable.h:494
float dt_aligned_pixel_simd_t __attribute__((vector_size(16), aligned(16)))
Enable aggressive floating-point arithmetic optimizations, in denormals handling. Set through user pr...
Definition darktable.h:524
#define dt_free(ptr)
Definition darktable.h:456
static void dt_get_times(dt_times_t *t)
Definition darktable.h:921
#define dt_pixelpipe_cache_free_align(mem)
Definition darktable.h:453
#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
#define dt_omploop_sfence()
Definition imageop.h:702
static gboolean dt_iop_colorspace_is_rgb(const dt_iop_colorspace_type_t cst)
Definition imageop.h:213
static void dt_iop_estimate_exp(const float *const x, const float *const y, const int num, float *coeff)
int dt_ioppr_get_iop_order(GList *iop_order_list, const char *op_name, const int multi_priority)
Return the iop_order for a given operation/instance pair.
Definition iop_order.c:868
static __DT_CLONE_TARGETS__ void _transform_lab_to_rgb_matrix(const float *const image_in, float *const image_out, const int width, const int height, const dt_iop_order_iccprofile_info_t *const profile_info)
static __DT_CLONE_TARGETS__ void _transform_matrix_rgb(const float *const restrict image_in, float *const restrict image_out, const int width, const int height, const dt_iop_order_iccprofile_info_t *const profile_info_from, const dt_iop_order_iccprofile_info_t *const profile_info_to)
static void _transform_matrix(struct dt_iop_module_t *self, const float *const restrict image_in, float *const restrict image_out, const int width, const int height, const dt_iop_colorspace_type_t cst_from, const dt_iop_colorspace_type_t cst_to, dt_iop_colorspace_type_t *converted_cst, const dt_iop_order_iccprofile_info_t *const profile_info)
dt_iop_order_iccprofile_info_t * dt_ioppr_get_pipe_output_profile_info(const struct dt_dev_pixelpipe_t *pipe)
static __DT_CLONE_TARGETS__ void _clear_lut_curves(dt_iop_order_iccprofile_info_t *const profile_info)
Definition iop_profile.c:67
dt_iop_order_iccprofile_info_t * dt_ioppr_add_profile_info_to_list(struct dt_develop_t *dev, const dt_colorspaces_color_profile_type_t profile_type, const char *profile_filename, const int intent)
void dt_ioppr_transform_image_colorspace(struct dt_iop_module_t *self, const float *const image_in, float *const image_out, const int width, const int height, const int cst_from, const int cst_to, int *converted_cst, const dt_iop_order_iccprofile_info_t *const profile_info)
#define DT_IOPPR_LUT_SAMPLES
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_work_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)
static void _transform_lcms2(struct dt_iop_module_t *self, const float *const image_in, float *const image_out, const int width, const int height, const int cst_from, const int cst_to, int *converted_cst, const dt_iop_order_iccprofile_info_t *const profile_info)
static __DT_CLONE_TARGETS__ int dt_ioppr_generate_profile_info(dt_iop_order_iccprofile_info_t *profile_info, const int type, const char *filename, const int intent)
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_current_profile_info(dt_iop_module_t *module, const struct dt_dev_pixelpipe_t *pipe)
int dt_ioppr_transform_image_colorspace_cl(struct dt_iop_module_t *self, const int devid, cl_mem dev_img_in, cl_mem dev_img_out, const int width, const int height, const int cst_from, const int cst_to, int *converted_cst, const dt_iop_order_iccprofile_info_t *const profile_info)
__DT_CLONE_TARGETS__ dt_iop_order_iccprofile_info_t * dt_ioppr_get_profile_info_from_list(struct dt_develop_t *dev, const dt_colorspaces_color_profile_type_t profile_type, const char *profile_filename)
void dt_ioppr_transform_image_colorspace_rgb(const float *const restrict image_in, float *const restrict image_out, const int width, const int height, const dt_iop_order_iccprofile_info_t *const profile_info_from, const dt_iop_order_iccprofile_info_t *const profile_info_to, const char *message)
void dt_ioppr_get_profile_info_cl(const dt_iop_order_iccprofile_info_t *const profile_info, dt_colorspaces_iccprofile_info_cl_t *profile_info_cl)
__DT_CLONE_TARGETS__ void dt_ioppr_init_profile_info(dt_iop_order_iccprofile_info_t *profile_info, const int lutsize)
void dt_colorspaces_free_cl_global(dt_colorspaces_cl_global_t *g)
dt_iop_order_iccprofile_info_t * dt_ioppr_get_pipe_input_profile_info(const struct dt_dev_pixelpipe_t *pipe)
static __DT_CLONE_TARGETS__ void _transform_rgb_to_lab_matrix(const float *const restrict image_in, float *const restrict image_out, const int width, const int height, const dt_iop_order_iccprofile_info_t *const profile_info)
static void _transform_from_to_rgb_lab_lcms2(const float *const image_in, float *const image_out, const int width, const int height, const dt_colorspaces_color_profile_type_t type, const char *filename, const int intent, const int direction)
Definition iop_profile.c:76
void dt_ioppr_free_iccprofile_params_cl(dt_colorspaces_iccprofile_info_cl_t **_profile_info_cl, cl_float **_profile_lut_cl, cl_mem *_dev_profile_info, cl_mem *_dev_profile_lut)
static int _init_unbounded_coeffs(float *const lutr, float *const lutg, float *const lutb, float *const unbounded_coeffsr, float *const unbounded_coeffsg, float *const unbounded_coeffsb, const int lutsize)
static void _apply_tonecurves(const float *const image_in, float *const image_out, const int width, const int height, const float *const restrict lutr, const float *const restrict lutg, const float *const restrict lutb, const float *const restrict unbounded_coeffsr, const float *const restrict unbounded_coeffsg, const float *const restrict unbounded_coeffsb, const int lutsize)
dt_colorspaces_cl_global_t * dt_colorspaces_init_cl_global()
dt_iop_order_iccprofile_info_t * dt_ioppr_get_iop_work_profile_info(struct dt_iop_module_t *module, GList *iop_list)
cl_float * dt_ioppr_get_trc_cl(const dt_iop_order_iccprofile_info_t *const profile_info)
void dt_ioppr_cleanup_profile_info(dt_iop_order_iccprofile_info_t *profile_info)
int dt_ioppr_transform_image_colorspace_rgb_cl(const int devid, cl_mem dev_img_in, cl_mem dev_img_out, const int width, const int height, const dt_iop_order_iccprofile_info_t *const profile_info_from, const dt_iop_order_iccprofile_info_t *const profile_info_to, const char *message)
cl_int dt_ioppr_build_iccprofile_params_cl(const dt_iop_order_iccprofile_info_t *const profile_info, const int devid, dt_colorspaces_iccprofile_info_cl_t **_profile_info_cl, cl_float **_profile_lut_cl, cl_mem *_dev_profile_info, cl_mem *_dev_profile_lut)
dt_iop_order_iccprofile_info_t * dt_ioppr_set_pipe_input_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, const dt_colormatrix_t matrix_in)
void dt_ioppr_get_export_profile_type(struct dt_develop_t *dev, dt_colorspaces_color_profile_type_t *profile_type, const char **profile_filename)
static const float x
const float *const lut
const float const int lutsize
static dt_aligned_pixel_t float *const const float unbounded_coeffs[3][3]
float *const restrict const size_t k
float *const restrict const size_t const size_t ch
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
static int mat3SSEinv(dt_colormatrix_t dst, const dt_colormatrix_t src)
Definition matrices.h:36
float dt_aligned_pixel_t[4]
int dt_opencl_enqueue_kernel_2d(const int dev, const int kernel, const size_t *sizes)
Definition opencl.c:2136
int dt_opencl_copy_device_to_host(const int devid, void *host, void *device, const int width, const int height, const int bpp)
Definition opencl.c:2163
void * dt_opencl_alloc_device(const int devid, const int width, const int height, const int bpp)
Definition opencl.c:2471
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
int dt_opencl_write_host_to_device(const int devid, void *host, void *device, const int width, const int height, const int bpp)
Definition opencl.c:2216
#define ROUNDUPDHT(a, b)
Definition opencl.h:82
#define ROUNDUPDWD(a, b)
Definition opencl.h:81
struct dt_colorspaces_t * color_profiles
Definition darktable.h:788
GList * iop
Definition darktable.h:761
struct dt_opencl_t * opencl
Definition darktable.h:785
int32_t unmuted
Definition darktable.h:760
int kernel_colorspaces_transform_rgb_matrix_to_rgb
int kernel_colorspaces_transform_lab_to_rgb_matrix
int kernel_colorspaces_transform_rgb_matrix_to_lab
pthread_rwlock_t xprofile_lock
struct dt_iop_order_iccprofile_info_t * work_profile_info
struct dt_iop_order_iccprofile_info_t * input_profile_info
struct dt_iop_order_iccprofile_info_t * output_profile_info
GList * iop_order_list
Definition develop.h:285
GList * iop
Definition develop.h:279
GList * allprofile_info
Definition develop.h:314
GModule *dt_dev_operation_t op
Definition imageop.h:230
char multi_name[128]
Definition imageop.h:363
struct dt_develop_t * dev
Definition imageop.h:296
GModule *dt_dev_operation_t op
Definition imageop.h:256
dt_iop_params_t * params
Definition imageop.h:307
dt_iop_color_intent_t intent
Definition iop_profile.h:55
dt_colormatrix_t matrix_out_transposed
Definition iop_profile.h:66
dt_colorspaces_color_profile_type_t type
Definition iop_profile.h:53
char filename[DT_IOP_COLOR_ICC_LEN]
Definition iop_profile.h:54
dt_colormatrix_t matrix_in_transposed
Definition iop_profile.h:65
struct dt_colorspaces_cl_global_t * colorspaces
Definition opencl.h:272
double clock
Definition darktable.h:842
double user
Definition darktable.h:843