1 /*
2 This file is part of darktable,
3 Copyright (C) 2020 darktable developers.
4
5 darktable is free software: you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
9
10 darktable is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public License
16 along with darktable. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 #ifndef DT_IOP_PROFILE_H
20 #define DT_IOP_PROFILE_H
21
22 #include "common/colorspaces_inline_conversions.h"
23 #include "common/colorspaces.h"
24 #include "develop/imageop.h"
25
26 #ifdef HAVE_CONFIG_H
27 #include "config.h"
28 #endif
29
30 #ifdef HAVE_OPENCL
31 #include <CL/cl.h> // for cl_mem
32 #endif
33
34 struct dt_iop_module_t;
35 struct dt_develop_t;
36 struct dt_dev_pixelpipe_t;
37 struct dt_dev_pixelpipe_iop_t;
38
39 typedef struct dt_iop_order_iccprofile_info_t
40 {
41 dt_colorspaces_color_profile_type_t type;
42 char filename[DT_IOP_COLOR_ICC_LEN];
43 dt_iop_color_intent_t intent;
44 dt_colormatrix_t matrix_in; // don't align on more than 16 bits or OpenCL will fail
45 dt_colormatrix_t matrix_out;
46 int lutsize;
47 float *lut_in[3];
48 float *lut_out[3];
49 float unbounded_coeffs_in[3][3] DT_ALIGNED_PIXEL;
50 float unbounded_coeffs_out[3][3] DT_ALIGNED_PIXEL;
51 int nonlinearlut;
52 float grey;
53 dt_colormatrix_t matrix_in_transposed; // same as matrix_in, but stored such as to permit vectorization
54 dt_colormatrix_t matrix_out_transposed; // same as matrix_out, but stored such as to permit vectorization
55 } dt_iop_order_iccprofile_info_t;
56
57 /** must be called before using profile_info, default lutsize = 0 */
58 void dt_ioppr_init_profile_info(dt_iop_order_iccprofile_info_t *profile_info, const int lutsize);
59 /** must be called when done with profile_info */
60 void dt_ioppr_cleanup_profile_info(dt_iop_order_iccprofile_info_t *profile_info);
61
62 /** returns the profile info from dev profiles info list that matches (profile_type, profile_filename)
63 * NULL if not found
64 */
65 dt_iop_order_iccprofile_info_t *
66 dt_ioppr_get_profile_info_from_list(struct dt_develop_t *dev, dt_colorspaces_color_profile_type_t profile_type,
67 const char *profile_filename);
68
69 /** adds the profile info from (profile_type, profile_filename) to the dev profiles info list if not already exists
70 * returns the generated profile or the existing one
71 */
72 dt_iop_order_iccprofile_info_t *
73 dt_ioppr_add_profile_info_to_list(struct dt_develop_t *dev,
74 const dt_colorspaces_color_profile_type_t profile_type,
75 const char *profile_filename,
76 const int intent);
77
78 /** returns a reference to the work profile info as set on colorin iop
79 * only if module is between colorin and colorout, otherwise returns NULL
80 * work profile must not be cleanup()
81 */
82 dt_iop_order_iccprofile_info_t *dt_ioppr_get_iop_work_profile_info(struct dt_iop_module_t *module,
83 GList *iop_list);
84 dt_iop_order_iccprofile_info_t *dt_ioppr_get_iop_input_profile_info(struct dt_iop_module_t *module,
85 GList *iop_list);
86
87 /** set the work profile (type, filename) on the pipe, should be called on process*()
88 * if matrix cannot be generated it default to linear rec 2020
89 * returns the actual profile that has been set
90 */
91 dt_iop_order_iccprofile_info_t *
92 dt_ioppr_set_pipe_work_profile_info(struct dt_develop_t *dev,
93 struct dt_dev_pixelpipe_t *pipe,
94 const dt_colorspaces_color_profile_type_t type,
95 const char *filename,
96 const int intent);
97
98 dt_iop_order_iccprofile_info_t *
99 dt_ioppr_set_pipe_input_profile_info(struct dt_develop_t *dev,
100 struct dt_dev_pixelpipe_t *pipe,
101 const dt_colorspaces_color_profile_type_t type,
102 const char *filename,
103 const int intent, const dt_colormatrix_t matrix_in);
104
105 dt_iop_order_iccprofile_info_t *
106 dt_ioppr_set_pipe_output_profile_info(struct dt_develop_t *dev,
107 struct dt_dev_pixelpipe_t *pipe,
108 const dt_colorspaces_color_profile_type_t type,
109 const char *filename,
110 const int intent);
111
112 /** returns a reference to the histogram profile info
113 * histogram profile must not be cleanup()
114 */
115 dt_iop_order_iccprofile_info_t *dt_ioppr_get_histogram_profile_info(struct dt_develop_t *dev);
116
117 /** returns the active work/input/output profile on the pipe */
118 dt_iop_order_iccprofile_info_t *dt_ioppr_get_pipe_work_profile_info(struct dt_dev_pixelpipe_t *pipe);
119 dt_iop_order_iccprofile_info_t *dt_ioppr_get_pipe_input_profile_info(struct dt_dev_pixelpipe_t *pipe);
120 dt_iop_order_iccprofile_info_t *dt_ioppr_get_pipe_output_profile_info(struct dt_dev_pixelpipe_t *pipe);
121
122 /** Get the relevant RGB -> XYZ profile at the position of current module */
123 dt_iop_order_iccprofile_info_t *dt_ioppr_get_pipe_current_profile_info(struct dt_iop_module_t *module,
124 struct dt_dev_pixelpipe_t *pipe);
125
126 /** returns the current setting of the work profile on colorin iop */
127 void dt_ioppr_get_work_profile_type(struct dt_develop_t *dev,
128 dt_colorspaces_color_profile_type_t *profile_type,
129 const char **profile_filename);
130 /** returns the current setting of the input profile on colorin iop */
131 void dt_ioppr_get_input_profile_type(struct dt_develop_t *dev,
132 dt_colorspaces_color_profile_type_t *profile_type,
133 const char **profile_filename);
134 /** returns the current setting of the export profile on colorout iop */
135 void dt_ioppr_get_export_profile_type(struct dt_develop_t *dev,
136 dt_colorspaces_color_profile_type_t *profile_type,
137 const char **profile_filename);
138 /** returns the current setting of the histogram profile */
139 void dt_ioppr_get_histogram_profile_type(dt_colorspaces_color_profile_type_t *profile_type,
140 const char **profile_filename);
141
142 /** transforms image from cst_from to cst_to colorspace using profile_info */
143 void dt_ioppr_transform_image_colorspace(struct dt_iop_module_t *self, const float *const image_in,
144 float *const image_out, const int width, const int height,
145 const int cst_from, const int cst_to, int *converted_cst,
146 const dt_iop_order_iccprofile_info_t *const profile_info);
147
148 void dt_ioppr_transform_image_colorspace_rgb(const float *const image_in, float *const image_out, const int width,
149 const int height,
150 const dt_iop_order_iccprofile_info_t *const profile_info_from,
151 const dt_iop_order_iccprofile_info_t *const profile_info_to,
152 const char *message);
153
154 #ifdef HAVE_OPENCL
155 typedef struct dt_colorspaces_cl_global_t
156 {
157 int kernel_colorspaces_transform_lab_to_rgb_matrix;
158 int kernel_colorspaces_transform_rgb_matrix_to_lab;
159 int kernel_colorspaces_transform_rgb_matrix_to_rgb;
160 } dt_colorspaces_cl_global_t;
161
162 // must be in synch with colorspaces.cl dt_colorspaces_iccprofile_info_cl_t
163 typedef struct dt_colorspaces_iccprofile_info_cl_t
164 {
165 cl_float matrix_in[9];
166 cl_float matrix_out[9];
167 cl_int lutsize;
168 cl_float unbounded_coeffs_in[3][3];
169 cl_float unbounded_coeffs_out[3][3];
170 cl_int nonlinearlut;
171 cl_float grey;
172 } dt_colorspaces_iccprofile_info_cl_t;
173
174 dt_colorspaces_cl_global_t *dt_colorspaces_init_cl_global(void);
175 void dt_colorspaces_free_cl_global(dt_colorspaces_cl_global_t *g);
176
177 /** sets profile_info_cl using profile_info
178 * to be used as a parameter when calling opencl
179 */
180 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);
181 /** returns the profile_info trc
182 * to be used as a parameter when calling opencl
183 */
184 cl_float *dt_ioppr_get_trc_cl(const dt_iop_order_iccprofile_info_t *const profile_info);
185
186 /** build the required parameters for a kernel that uses a profile info */
187 cl_int dt_ioppr_build_iccprofile_params_cl(const dt_iop_order_iccprofile_info_t *const profile_info,
188 const int devid, dt_colorspaces_iccprofile_info_cl_t **_profile_info_cl,
189 cl_float **_profile_lut_cl, cl_mem *_dev_profile_info,
190 cl_mem *_dev_profile_lut);
191 /** free parameters build with the previous function */
192 void dt_ioppr_free_iccprofile_params_cl(dt_colorspaces_iccprofile_info_cl_t **_profile_info_cl,
193 cl_float **_profile_lut_cl, cl_mem *_dev_profile_info,
194 cl_mem *_dev_profile_lut);
195
196 /** same as the C version */
197 int dt_ioppr_transform_image_colorspace_cl(struct dt_iop_module_t *self, const int devid, cl_mem dev_img_in,
198 cl_mem dev_img_out, const int width, const int height,
199 const int cst_from, const int cst_to, int *converted_cst,
200 const dt_iop_order_iccprofile_info_t *const profile_info);
201
202 int dt_ioppr_transform_image_colorspace_rgb_cl(const int devid, cl_mem dev_img_in, cl_mem dev_img_out,
203 const int width, const int height,
204 const dt_iop_order_iccprofile_info_t *const profile_info_from,
205 const dt_iop_order_iccprofile_info_t *const profile_info_to,
206 const char *message);
207 #endif
208
209 /** the following must have the matrix_in and matrix_out generated */
210
211 #ifdef _OPENMP
212 #pragma omp declare simd aligned(lut:64)
213 #endif
extrapolate_lut(const float * const lut,const float v,const int lutsize)214 static inline float extrapolate_lut(const float *const lut, const float v, const int lutsize)
215 {
216 // TODO: check if optimization is worthwhile!
217 const float ft = CLAMPS(v * (lutsize - 1), 0, lutsize - 1);
218 const int t = (ft < lutsize - 2) ? ft : lutsize - 2;
219 const float f = ft - t;
220 const float l1 = lut[t];
221 const float l2 = lut[t + 1];
222 return l1 * (1.0f - f) + l2 * f;
223 }
224
225
226 #ifdef _OPENMP
227 #pragma omp declare simd
228 #endif
eval_exp(const float coeff[3],const float x)229 static inline float eval_exp(const float coeff[3], const float x)
230 {
231 return coeff[1] * powf(x * coeff[0], coeff[2]);
232 }
233
234
235 #ifdef _OPENMP
236 #pragma omp declare simd \
237 aligned(rgb_in, rgb_out, unbounded_coeffs:16) \
238 aligned(lut:64) \
239 uniform(rgb_in, rgb_out, unbounded_coeffs, lut)
240 #endif
_apply_trc(const dt_aligned_pixel_t rgb_in,dt_aligned_pixel_t rgb_out,float * const lut[3],const float unbounded_coeffs[3][3],const int lutsize)241 static inline void _apply_trc(const dt_aligned_pixel_t rgb_in, dt_aligned_pixel_t rgb_out,
242 float *const lut[3],
243 const float unbounded_coeffs[3][3],
244 const int lutsize)
245 {
246 for(int c = 0; c < 3; c++)
247 {
248 rgb_out[c] = (lut[c][0] >= 0.0f) ? ((rgb_in[c] < 1.0f) ? extrapolate_lut(lut[c], rgb_in[c], lutsize)
249 : eval_exp(unbounded_coeffs[c], rgb_in[c]))
250 : rgb_in[c];
251 }
252 }
253
254 #ifdef _OPENMP
255 #pragma omp declare simd \
256 aligned(rgb, matrix_in, unbounded_coeffs_in:16) \
257 aligned(lut_in:64) \
258 uniform(rgb, matrix_in, lut_in, unbounded_coeffs_in)
259 #endif
dt_ioppr_get_rgb_matrix_luminance(const dt_aligned_pixel_t rgb,const dt_colormatrix_t matrix_in,float * const lut_in[3],const float unbounded_coeffs_in[3][3],const int lutsize,const int nonlinearlut)260 static inline float dt_ioppr_get_rgb_matrix_luminance(const dt_aligned_pixel_t rgb,
261 const dt_colormatrix_t matrix_in, float *const lut_in[3],
262 const float unbounded_coeffs_in[3][3],
263 const int lutsize, const int nonlinearlut)
264 {
265 float luminance = 0.f;
266
267 if(nonlinearlut)
268 {
269 dt_aligned_pixel_t linear_rgb;
270 _apply_trc(rgb, linear_rgb, lut_in, unbounded_coeffs_in, lutsize);
271 luminance = matrix_in[1][0] * linear_rgb[0] + matrix_in[1][1] * linear_rgb[1] + matrix_in[1][2] * linear_rgb[2];
272 }
273 else
274 luminance = matrix_in[1][0] * rgb[0] + matrix_in[1][1] * rgb[1] + matrix_in[1][2] * rgb[2];
275
276 return luminance;
277 }
278
279
280 #ifdef _OPENMP
281 #pragma omp declare simd \
282 aligned(unbounded_coeffs_in:16) \
283 aligned(lut_in:64) \
284 uniform(lut_in, unbounded_coeffs_in)
285 #endif
dt_ioppr_rgb_matrix_to_xyz(const dt_aligned_pixel_t rgb,dt_aligned_pixel_t xyz,const dt_colormatrix_t matrix_in_transposed,float * const lut_in[3],const float unbounded_coeffs_in[3][3],const int lutsize,const int nonlinearlut)286 static inline void dt_ioppr_rgb_matrix_to_xyz(const dt_aligned_pixel_t rgb, dt_aligned_pixel_t xyz,
287 const dt_colormatrix_t matrix_in_transposed, float *const lut_in[3],
288 const float unbounded_coeffs_in[3][3],
289 const int lutsize, const int nonlinearlut)
290 {
291 if(nonlinearlut)
292 {
293 dt_aligned_pixel_t linear_rgb;
294 _apply_trc(rgb, linear_rgb, lut_in, unbounded_coeffs_in, lutsize);
295 dt_apply_transposed_color_matrix(linear_rgb, matrix_in_transposed, xyz);
296 }
297 else
298 dt_apply_transposed_color_matrix(rgb, matrix_in_transposed, xyz);
299 }
300
301 #ifdef _OPENMP
302 #pragma omp declare simd \
303 aligned(unbounded_coeffs_out:16) \
304 aligned(lut_out:64) \
305 uniform(lut_out, unbounded_coeffs_out)
306 #endif
dt_ioppr_xyz_to_rgb_matrix(const dt_aligned_pixel_t xyz,dt_aligned_pixel_t rgb,const dt_colormatrix_t matrix_out_transposed,float * const lut_out[3],const float unbounded_coeffs_out[3][3],const int lutsize,const int nonlinearlut)307 static inline void dt_ioppr_xyz_to_rgb_matrix(const dt_aligned_pixel_t xyz, dt_aligned_pixel_t rgb,
308 const dt_colormatrix_t matrix_out_transposed, float *const lut_out[3],
309 const float unbounded_coeffs_out[3][3],
310 const int lutsize, const int nonlinearlut)
311 {
312 if(nonlinearlut)
313 {
314 dt_aligned_pixel_t linear_rgb;
315 dt_apply_transposed_color_matrix(xyz, matrix_out_transposed, linear_rgb);
316 _apply_trc(linear_rgb, rgb, lut_out, unbounded_coeffs_out, lutsize);
317 }
318 else
319 dt_apply_transposed_color_matrix(xyz, matrix_out_transposed, rgb);
320 }
321
322
323 #ifdef _OPENMP
324 #pragma omp declare simd \
325 aligned(unbounded_coeffs_out:16) \
326 aligned(lut_out:64) \
327 uniform(lut_out, unbounded_coeffs_out)
328 #endif
dt_ioppr_lab_to_rgb_matrix(const dt_aligned_pixel_t lab,dt_aligned_pixel_t rgb,const dt_colormatrix_t matrix_out_transposed,float * const lut_out[3],const float unbounded_coeffs_out[3][3],const int lutsize,const int nonlinearlut)329 static inline void dt_ioppr_lab_to_rgb_matrix(const dt_aligned_pixel_t lab, dt_aligned_pixel_t rgb,
330 const dt_colormatrix_t matrix_out_transposed, float *const lut_out[3],
331 const float unbounded_coeffs_out[3][3],
332 const int lutsize, const int nonlinearlut)
333 {
334 dt_aligned_pixel_t xyz;
335 dt_Lab_to_XYZ(lab, xyz);
336
337 if(nonlinearlut)
338 {
339 dt_aligned_pixel_t linear_rgb;
340 dt_apply_transposed_color_matrix(xyz, matrix_out_transposed, linear_rgb);
341 _apply_trc(linear_rgb, rgb, lut_out, unbounded_coeffs_out, lutsize);
342 }
343 else
344 {
345 dt_apply_transposed_color_matrix(xyz, matrix_out_transposed, rgb);
346 }
347 }
348
349 #ifdef _OPENMP
350 #pragma omp declare simd \
351 aligned(unbounded_coeffs_in:16) \
352 aligned(lut_in:64) \
353 uniform(lut_in, unbounded_coeffs_in)
354 #endif
dt_ioppr_rgb_matrix_to_lab(const dt_aligned_pixel_t rgb,dt_aligned_pixel_t lab,const dt_colormatrix_t matrix_in_transposed,float * const lut_in[3],const float unbounded_coeffs_in[3][3],const int lutsize,const int nonlinearlut)355 static inline void dt_ioppr_rgb_matrix_to_lab(const dt_aligned_pixel_t rgb, dt_aligned_pixel_t lab,
356 const dt_colormatrix_t matrix_in_transposed, float *const lut_in[3],
357 const float unbounded_coeffs_in[3][3],
358 const int lutsize, const int nonlinearlut)
359 {
360 dt_aligned_pixel_t xyz = { 0.f };
361 dt_ioppr_rgb_matrix_to_xyz(rgb, xyz, matrix_in_transposed, lut_in, unbounded_coeffs_in, lutsize, nonlinearlut);
362 dt_XYZ_to_Lab(xyz, lab);
363 }
364
dt_ioppr_get_profile_info_middle_grey(const dt_iop_order_iccprofile_info_t * const profile_info)365 static inline float dt_ioppr_get_profile_info_middle_grey(const dt_iop_order_iccprofile_info_t *const profile_info)
366 {
367 return profile_info->grey;
368 }
369
370 #ifdef _OPENMP
371 #pragma omp declare simd
372 #endif
dt_ioppr_compensate_middle_grey(const float x,const dt_iop_order_iccprofile_info_t * const profile_info)373 static inline float dt_ioppr_compensate_middle_grey(const float x, const dt_iop_order_iccprofile_info_t *const profile_info)
374 {
375 // we transform the curve nodes from the image colorspace to lab
376 dt_aligned_pixel_t lab = { 0.0f };
377 const dt_aligned_pixel_t rgb = { x, x, x };
378 dt_ioppr_rgb_matrix_to_lab(rgb, lab, profile_info->matrix_in_transposed, profile_info->lut_in,
379 profile_info->unbounded_coeffs_in, profile_info->lutsize, profile_info->nonlinearlut);
380 return lab[0] * .01f;
381 }
382
383 #ifdef _OPENMP
384 #pragma omp declare simd
385 #endif
dt_ioppr_uncompensate_middle_grey(const float x,const dt_iop_order_iccprofile_info_t * const profile_info)386 static inline float dt_ioppr_uncompensate_middle_grey(const float x, const dt_iop_order_iccprofile_info_t *const profile_info)
387 {
388 // we transform the curve nodes from lab to the image colorspace
389 const dt_aligned_pixel_t lab = { x * 100.f, 0.0f, 0.0f };
390 dt_aligned_pixel_t rgb = { 0.0f };
391
392 dt_ioppr_lab_to_rgb_matrix(lab, rgb, profile_info->matrix_out_transposed, profile_info->lut_out,
393 profile_info->unbounded_coeffs_out, profile_info->lutsize, profile_info->nonlinearlut);
394 return rgb[0];
395 }
396
397 #endif
398