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