Ansel 0.0
A darktable fork - bloat + design vision
Loading...
Searching...
No Matches
common/lut3d.c
Go to the documentation of this file.
1/*
2 This file is part of Ansel,
3 Copyright (C) 2026 Aurélien PIERRE.
4
5 Ansel is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
9
10 Ansel is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with darktable. If not, see <http://www.gnu.org/licenses/>.
17*/
18
19#include "common/darktable.h"
20#include "common/lut3d.h"
21
22#include <math.h>
23
24static inline void _prepare_lut_input(const float *const input, float normalized[3], float residual[3],
25 float rgbd[3], int rgbi[3], const uint16_t level,
26 const float safe_normalization)
27{
28 for(int c = 0; c < 3; c++)
29 {
30 const float unclamped = input[c] / safe_normalization;
31 normalized[c] = fminf(fmaxf(unclamped, 0.f), 1.f);
32 residual[c] = unclamped - normalized[c];
33 }
34
35 for(int c = 0; c < 3; c++)
36 rgbd[c] = normalized[c] * (float)(level - 1);
37
38 for(int c = 0; c < 3; c++)
39 rgbi[c] = ((int)rgbd[c] < 0) ? 0 : (((int)rgbd[c] > level - 2) ? level - 2 : (int)rgbd[c]);
40
41 for(int c = 0; c < 3; c++)
42 rgbd[c] -= rgbi[c];
43}
44
45static inline __attribute__((always_inline)) void _finish_lut_output(const float *const input, float *const output, const float residual[3],
46 const float safe_normalization)
47{
55 output[0] = (output[0] + residual[0]) * safe_normalization;
56 output[1] = (output[1] + residual[1]) * safe_normalization;
57 output[2] = (output[2] + residual[2]) * safe_normalization;
58 output[3] = input[3];
59}
60
62void dt_lut3d_tetrahedral_interp(const float *const in, float *const out, const size_t pixel_nb,
63 const float *const restrict clut, const uint16_t level,
64 const float normalization)
65{
66 const int level2 = level * level;
67 const float safe_normalization = fmaxf(normalization, 1e-6f);
69 for(size_t k = 0; k < (size_t)(pixel_nb * 4); k += 4)
70 {
71 const float *const input = in + k;
72 float *const output = out + k;
73 float normalized[3];
74 float residual[3];
75 float rgbd[3];
76 int rgbi[3];
77
78 _prepare_lut_input(input, normalized, residual, rgbd, rgbi, level, safe_normalization);
79
80 const int color = rgbi[0] + rgbi[1] * level + rgbi[2] * level2;
81 const int i000 = color * 3;
82 const int i100 = i000 + 3;
83 const int i010 = (color + level) * 3;
84 const int i110 = i010 + 3;
85 const int i001 = (color + level2) * 3;
86 const int i101 = i001 + 3;
87 const int i011 = (color + level + level2) * 3;
88 const int i111 = i011 + 3;
89
90 if(rgbd[0] > rgbd[1])
91 {
92 if(rgbd[1] > rgbd[2])
93 {
94 output[0] = (1 - rgbd[0]) * clut[i000] + (rgbd[0] - rgbd[1]) * clut[i100]
95 + (rgbd[1] - rgbd[2]) * clut[i110] + rgbd[2] * clut[i111];
96 output[1] = (1 - rgbd[0]) * clut[i000 + 1] + (rgbd[0] - rgbd[1]) * clut[i100 + 1]
97 + (rgbd[1] - rgbd[2]) * clut[i110 + 1] + rgbd[2] * clut[i111 + 1];
98 output[2] = (1 - rgbd[0]) * clut[i000 + 2] + (rgbd[0] - rgbd[1]) * clut[i100 + 2]
99 + (rgbd[1] - rgbd[2]) * clut[i110 + 2] + rgbd[2] * clut[i111 + 2];
100 }
101 else if(rgbd[0] > rgbd[2])
102 {
103 output[0] = (1 - rgbd[0]) * clut[i000] + (rgbd[0] - rgbd[2]) * clut[i100]
104 + (rgbd[2] - rgbd[1]) * clut[i101] + rgbd[1] * clut[i111];
105 output[1] = (1 - rgbd[0]) * clut[i000 + 1] + (rgbd[0] - rgbd[2]) * clut[i100 + 1]
106 + (rgbd[2] - rgbd[1]) * clut[i101 + 1] + rgbd[1] * clut[i111 + 1];
107 output[2] = (1 - rgbd[0]) * clut[i000 + 2] + (rgbd[0] - rgbd[2]) * clut[i100 + 2]
108 + (rgbd[2] - rgbd[1]) * clut[i101 + 2] + rgbd[1] * clut[i111 + 2];
109 }
110 else
111 {
112 output[0] = (1 - rgbd[2]) * clut[i000] + (rgbd[2] - rgbd[0]) * clut[i001]
113 + (rgbd[0] - rgbd[1]) * clut[i101] + rgbd[1] * clut[i111];
114 output[1] = (1 - rgbd[2]) * clut[i000 + 1] + (rgbd[2] - rgbd[0]) * clut[i001 + 1]
115 + (rgbd[0] - rgbd[1]) * clut[i101 + 1] + rgbd[1] * clut[i111 + 1];
116 output[2] = (1 - rgbd[2]) * clut[i000 + 2] + (rgbd[2] - rgbd[0]) * clut[i001 + 2]
117 + (rgbd[0] - rgbd[1]) * clut[i101 + 2] + rgbd[1] * clut[i111 + 2];
118 }
119 }
120 else
121 {
122 if(rgbd[2] > rgbd[1])
123 {
124 output[0] = (1 - rgbd[2]) * clut[i000] + (rgbd[2] - rgbd[1]) * clut[i001]
125 + (rgbd[1] - rgbd[0]) * clut[i011] + rgbd[0] * clut[i111];
126 output[1] = (1 - rgbd[2]) * clut[i000 + 1] + (rgbd[2] - rgbd[1]) * clut[i001 + 1]
127 + (rgbd[1] - rgbd[0]) * clut[i011 + 1] + rgbd[0] * clut[i111 + 1];
128 output[2] = (1 - rgbd[2]) * clut[i000 + 2] + (rgbd[2] - rgbd[1]) * clut[i001 + 2]
129 + (rgbd[1] - rgbd[0]) * clut[i011 + 2] + rgbd[0] * clut[i111 + 2];
130 }
131 else if(rgbd[2] > rgbd[0])
132 {
133 output[0] = (1 - rgbd[1]) * clut[i000] + (rgbd[1] - rgbd[2]) * clut[i010]
134 + (rgbd[2] - rgbd[0]) * clut[i011] + rgbd[0] * clut[i111];
135 output[1] = (1 - rgbd[1]) * clut[i000 + 1] + (rgbd[1] - rgbd[2]) * clut[i010 + 1]
136 + (rgbd[2] - rgbd[0]) * clut[i011 + 1] + rgbd[0] * clut[i111 + 1];
137 output[2] = (1 - rgbd[1]) * clut[i000 + 2] + (rgbd[1] - rgbd[2]) * clut[i010 + 2]
138 + (rgbd[2] - rgbd[0]) * clut[i011 + 2] + rgbd[0] * clut[i111 + 2];
139 }
140 else
141 {
142 output[0] = (1 - rgbd[1]) * clut[i000] + (rgbd[1] - rgbd[0]) * clut[i010]
143 + (rgbd[0] - rgbd[2]) * clut[i110] + rgbd[2] * clut[i111];
144 output[1] = (1 - rgbd[1]) * clut[i000 + 1] + (rgbd[1] - rgbd[0]) * clut[i010 + 1]
145 + (rgbd[0] - rgbd[2]) * clut[i110 + 1] + rgbd[2] * clut[i111 + 1];
146 output[2] = (1 - rgbd[1]) * clut[i000 + 2] + (rgbd[1] - rgbd[0]) * clut[i010 + 2]
147 + (rgbd[0] - rgbd[2]) * clut[i110 + 2] + rgbd[2] * clut[i111 + 2];
148 }
149 }
150
151 _finish_lut_output(input, output, residual, safe_normalization);
152 }
153}
154
156void dt_lut3d_trilinear_interp(const float *const in, float *const out, const size_t pixel_nb,
157 const float *const restrict clut, const uint16_t level,
158 const float normalization)
159{
160 const int level2 = level * level;
161 const float safe_normalization = fmaxf(normalization, 1e-6f);
163 for(size_t k = 0; k < (size_t)(pixel_nb * 4); k += 4)
164 {
165 const float *const input = in + k;
166 float *const output = out + k;
167 float normalized[3];
168 float residual[3];
169 float rgbd[3];
170 int rgbi[3];
171 float tmp[6];
172
173 _prepare_lut_input(input, normalized, residual, rgbd, rgbi, level, safe_normalization);
174
175 const int color = rgbi[0] + rgbi[1] * level + rgbi[2] * level2;
176 int i = color * 3;
177 int j = (color + 1) * 3;
178
179 tmp[0] = clut[i] * (1 - rgbd[0]) + clut[j] * rgbd[0];
180 tmp[1] = clut[i + 1] * (1 - rgbd[0]) + clut[j + 1] * rgbd[0];
181 tmp[2] = clut[i + 2] * (1 - rgbd[0]) + clut[j + 2] * rgbd[0];
182
183 i = (color + level) * 3;
184 j = (color + level + 1) * 3;
185
186 tmp[3] = clut[i] * (1 - rgbd[0]) + clut[j] * rgbd[0];
187 tmp[4] = clut[i + 1] * (1 - rgbd[0]) + clut[j + 1] * rgbd[0];
188 tmp[5] = clut[i + 2] * (1 - rgbd[0]) + clut[j + 2] * rgbd[0];
189
190 output[0] = tmp[0] * (1 - rgbd[1]) + tmp[3] * rgbd[1];
191 output[1] = tmp[1] * (1 - rgbd[1]) + tmp[4] * rgbd[1];
192 output[2] = tmp[2] * (1 - rgbd[1]) + tmp[5] * rgbd[1];
193
194 i = (color + level2) * 3;
195 j = (color + level2 + 1) * 3;
196
197 tmp[0] = clut[i] * (1 - rgbd[0]) + clut[j] * rgbd[0];
198 tmp[1] = clut[i + 1] * (1 - rgbd[0]) + clut[j + 1] * rgbd[0];
199 tmp[2] = clut[i + 2] * (1 - rgbd[0]) + clut[j + 2] * rgbd[0];
200
201 i = (color + level + level2) * 3;
202 j = (color + level + level2 + 1) * 3;
203
204 tmp[3] = clut[i] * (1 - rgbd[0]) + clut[j] * rgbd[0];
205 tmp[4] = clut[i + 1] * (1 - rgbd[0]) + clut[j + 1] * rgbd[0];
206 tmp[5] = clut[i + 2] * (1 - rgbd[0]) + clut[j + 2] * rgbd[0];
207
208 tmp[0] = tmp[0] * (1 - rgbd[1]) + tmp[3] * rgbd[1];
209 tmp[1] = tmp[1] * (1 - rgbd[1]) + tmp[4] * rgbd[1];
210 tmp[2] = tmp[2] * (1 - rgbd[1]) + tmp[5] * rgbd[1];
211
212 output[0] = output[0] * (1 - rgbd[2]) + tmp[0] * rgbd[2];
213 output[1] = output[1] * (1 - rgbd[2]) + tmp[1] * rgbd[2];
214 output[2] = output[2] * (1 - rgbd[2]) + tmp[2] * rgbd[2];
215
216 _finish_lut_output(input, output, residual, safe_normalization);
217 }
218}
219
221void dt_lut3d_pyramid_interp(const float *const in, float *const out, const size_t pixel_nb,
222 const float *const restrict clut, const uint16_t level,
223 const float normalization)
224{
225 const int level2 = level * level;
226 const float safe_normalization = fmaxf(normalization, 1e-6f);
228 for(size_t k = 0; k < (size_t)(pixel_nb * 4); k += 4)
229 {
230 const float *const input = in + k;
231 float *const output = out + k;
232 float normalized[3];
233 float residual[3];
234 float rgbd[3];
235 int rgbi[3];
236
237 _prepare_lut_input(input, normalized, residual, rgbd, rgbi, level, safe_normalization);
238
239 const int color = rgbi[0] + rgbi[1] * level + rgbi[2] * level2;
240 const int i000 = color * 3;
241 const int i100 = i000 + 3;
242 const int i010 = (color + level) * 3;
243 const int i110 = i010 + 3;
244 const int i001 = (color + level2) * 3;
245 const int i101 = i001 + 3;
246 const int i011 = (color + level + level2) * 3;
247 const int i111 = i011 + 3;
248
249 if(rgbd[1] > rgbd[0] && rgbd[2] > rgbd[0])
250 {
251 output[0] = clut[i000] + (clut[i111] - clut[i011]) * rgbd[0] + (clut[i010] - clut[i000]) * rgbd[1]
252 + (clut[i001] - clut[i000]) * rgbd[2]
253 + (clut[i011] - clut[i001] - clut[i010] + clut[i000]) * rgbd[1] * rgbd[2];
254 output[1] = clut[i000 + 1] + (clut[i111 + 1] - clut[i011 + 1]) * rgbd[0]
255 + (clut[i010 + 1] - clut[i000 + 1]) * rgbd[1]
256 + (clut[i001 + 1] - clut[i000 + 1]) * rgbd[2]
257 + (clut[i011 + 1] - clut[i001 + 1] - clut[i010 + 1] + clut[i000 + 1]) * rgbd[1] * rgbd[2];
258 output[2] = clut[i000 + 2] + (clut[i111 + 2] - clut[i011 + 2]) * rgbd[0]
259 + (clut[i010 + 2] - clut[i000 + 2]) * rgbd[1]
260 + (clut[i001 + 2] - clut[i000 + 2]) * rgbd[2]
261 + (clut[i011 + 2] - clut[i001 + 2] - clut[i010 + 2] + clut[i000 + 2]) * rgbd[1] * rgbd[2];
262 }
263 else if(rgbd[0] > rgbd[1] && rgbd[2] > rgbd[1])
264 {
265 output[0] = clut[i000] + (clut[i100] - clut[i000]) * rgbd[0] + (clut[i111] - clut[i101]) * rgbd[1]
266 + (clut[i001] - clut[i000]) * rgbd[2]
267 + (clut[i101] - clut[i001] - clut[i100] + clut[i000]) * rgbd[0] * rgbd[2];
268 output[1] = clut[i000 + 1] + (clut[i100 + 1] - clut[i000 + 1]) * rgbd[0]
269 + (clut[i111 + 1] - clut[i101 + 1]) * rgbd[1]
270 + (clut[i001 + 1] - clut[i000 + 1]) * rgbd[2]
271 + (clut[i101 + 1] - clut[i001 + 1] - clut[i100 + 1] + clut[i000 + 1]) * rgbd[0] * rgbd[2];
272 output[2] = clut[i000 + 2] + (clut[i100 + 2] - clut[i000 + 2]) * rgbd[0]
273 + (clut[i111 + 2] - clut[i101 + 2]) * rgbd[1]
274 + (clut[i001 + 2] - clut[i000 + 2]) * rgbd[2]
275 + (clut[i101 + 2] - clut[i001 + 2] - clut[i100 + 2] + clut[i000 + 2]) * rgbd[0] * rgbd[2];
276 }
277 else
278 {
279 output[0] = clut[i000] + (clut[i100] - clut[i000]) * rgbd[0] + (clut[i010] - clut[i000]) * rgbd[1]
280 + (clut[i111] - clut[i110]) * rgbd[2]
281 + (clut[i110] - clut[i100] - clut[i010] + clut[i000]) * rgbd[0] * rgbd[1];
282 output[1] = clut[i000 + 1] + (clut[i100 + 1] - clut[i000 + 1]) * rgbd[0]
283 + (clut[i010 + 1] - clut[i000 + 1]) * rgbd[1]
284 + (clut[i111 + 1] - clut[i110 + 1]) * rgbd[2]
285 + (clut[i110 + 1] - clut[i100 + 1] - clut[i010 + 1] + clut[i000 + 1]) * rgbd[0] * rgbd[1];
286 output[2] = clut[i000 + 2] + (clut[i100 + 2] - clut[i000 + 2]) * rgbd[0]
287 + (clut[i010 + 2] - clut[i000 + 2]) * rgbd[1]
288 + (clut[i111 + 2] - clut[i110 + 2]) * rgbd[2]
289 + (clut[i110 + 2] - clut[i100 + 2] - clut[i010 + 2] + clut[i000 + 2]) * rgbd[0] * rgbd[1];
290 }
291
292 _finish_lut_output(input, output, residual, safe_normalization);
293 }
294}
295
296void dt_lut3d_apply(const float *const in, float *const out, const size_t pixel_nb, const float *const clut,
297 const uint16_t level, const float normalization,
298 const dt_lut3d_interpolation_t interpolation)
299{
300 switch(interpolation)
301 {
303 dt_lut3d_trilinear_interp(in, out, pixel_nb, clut, level, normalization);
304 return;
306 dt_lut3d_pyramid_interp(in, out, pixel_nb, clut, level, normalization);
307 return;
309 default:
310 dt_lut3d_tetrahedral_interp(in, out, pixel_nb, clut, level, normalization);
311 return;
312 }
313}
const dt_colormatrix_t dt_aligned_pixel_t out
__DT_CLONE_TARGETS__ void dt_lut3d_pyramid_interp(const float *const in, float *const out, const size_t pixel_nb, const float *const restrict clut, const uint16_t level, const float normalization)
void dt_lut3d_apply(const float *const in, float *const out, const size_t pixel_nb, const float *const clut, const uint16_t level, const float normalization, const dt_lut3d_interpolation_t interpolation)
Apply one interpolation model over a packed RGB CLUT.
__DT_CLONE_TARGETS__ void dt_lut3d_tetrahedral_interp(const float *const in, float *const out, const size_t pixel_nb, const float *const restrict clut, const uint16_t level, const float normalization)
__DT_CLONE_TARGETS__ void dt_lut3d_trilinear_interp(const float *const in, float *const out, const size_t pixel_nb, const float *const restrict clut, const uint16_t level, const float normalization)
static void _prepare_lut_input(const float *const input, float normalized[3], float residual[3], float rgbd[3], int rgbi[3], const uint16_t level, const float safe_normalization)
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_CLONE_TARGETS__
Definition darktable.h:367
#define __OMP_PARALLEL_FOR__(...)
Definition darktable.h:258
float *const restrict const size_t k
dt_lut3d_interpolation_t
Definition lut3d.h:25
@ DT_LUT3D_INTERP_PYRAMID
Definition lut3d.h:28
@ DT_LUT3D_INTERP_TETRAHEDRAL
Definition lut3d.h:26
@ DT_LUT3D_INTERP_TRILINEAR
Definition lut3d.h:27