Ansel 0.0
A darktable fork - bloat + design vision
Loading...
Searching...
No Matches
pixel/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 "system/openmp.h"
21#include "pixel/lut3d.h"
22
23#include <math.h>
24
25static inline void _prepare_lut_input(const float *const input, float normalized[3], float residual[3],
26 float rgbd[3], int rgbi[3], const uint16_t level,
27 const float safe_normalization)
28{
29 for(int c = 0; c < 3; c++)
30 {
31 const float unclamped = input[c] / safe_normalization;
32 normalized[c] = fminf(fmaxf(unclamped, 0.f), 1.f);
34 }
35
36 for(int c = 0; c < 3; c++)
37 rgbd[c] = normalized[c] * (float)(level - 1);
38
39 for(int c = 0; c < 3; c++)
40 rgbi[c] = ((int)rgbd[c] < 0) ? 0 : (((int)rgbd[c] > level - 2) ? level - 2 : (int)rgbd[c]);
41
42 for(int c = 0; c < 3; c++)
43 rgbd[c] -= rgbi[c];
44}
45
46static inline __attribute__((always_inline)) void _finish_lut_output(const float *const input, float *const output, const float residual[3],
47 const float safe_normalization)
48{
56 output[0] = (output[0] + residual[0]) * safe_normalization;
57 output[1] = (output[1] + residual[1]) * safe_normalization;
58 output[2] = (output[2] + residual[2]) * safe_normalization;
59 output[3] = input[3];
60}
61
63void dt_lut3d_tetrahedral_interp(const float *const in, float *const out, const size_t pixel_nb,
64 const float *const restrict clut, const uint16_t level,
65 const float normalization)
66{
67 const int level2 = level * level;
68 const float safe_normalization = fmaxf(normalization, 1e-6f);
70 for(size_t k = 0; k < (size_t)(pixel_nb * 4); k += 4)
71 {
72 const float *const input = in + k;
73 float *const output = out + k;
74 float normalized[3];
75 float residual[3];
76 float rgbd[3];
77 int rgbi[3];
78
80
81 const int color = rgbi[0] + rgbi[1] * level + rgbi[2] * level2;
82 const int i000 = color * 3;
83 const int i100 = i000 + 3;
84 const int i010 = (color + level) * 3;
85 const int i110 = i010 + 3;
86 const int i001 = (color + level2) * 3;
87 const int i101 = i001 + 3;
88 const int i011 = (color + level + level2) * 3;
89 const int i111 = i011 + 3;
90
91 if(rgbd[0] > rgbd[1])
92 {
93 if(rgbd[1] > rgbd[2])
94 {
95 output[0] = (1 - rgbd[0]) * clut[i000] + (rgbd[0] - rgbd[1]) * clut[i100]
96 + (rgbd[1] - rgbd[2]) * clut[i110] + rgbd[2] * clut[i111];
97 output[1] = (1 - rgbd[0]) * clut[i000 + 1] + (rgbd[0] - rgbd[1]) * clut[i100 + 1]
98 + (rgbd[1] - rgbd[2]) * clut[i110 + 1] + rgbd[2] * clut[i111 + 1];
99 output[2] = (1 - rgbd[0]) * clut[i000 + 2] + (rgbd[0] - rgbd[1]) * clut[i100 + 2]
100 + (rgbd[1] - rgbd[2]) * clut[i110 + 2] + rgbd[2] * clut[i111 + 2];
101 }
102 else if(rgbd[0] > rgbd[2])
103 {
104 output[0] = (1 - rgbd[0]) * clut[i000] + (rgbd[0] - rgbd[2]) * clut[i100]
105 + (rgbd[2] - rgbd[1]) * clut[i101] + rgbd[1] * clut[i111];
106 output[1] = (1 - rgbd[0]) * clut[i000 + 1] + (rgbd[0] - rgbd[2]) * clut[i100 + 1]
107 + (rgbd[2] - rgbd[1]) * clut[i101 + 1] + rgbd[1] * clut[i111 + 1];
108 output[2] = (1 - rgbd[0]) * clut[i000 + 2] + (rgbd[0] - rgbd[2]) * clut[i100 + 2]
109 + (rgbd[2] - rgbd[1]) * clut[i101 + 2] + rgbd[1] * clut[i111 + 2];
110 }
111 else
112 {
113 output[0] = (1 - rgbd[2]) * clut[i000] + (rgbd[2] - rgbd[0]) * clut[i001]
114 + (rgbd[0] - rgbd[1]) * clut[i101] + rgbd[1] * clut[i111];
115 output[1] = (1 - rgbd[2]) * clut[i000 + 1] + (rgbd[2] - rgbd[0]) * clut[i001 + 1]
116 + (rgbd[0] - rgbd[1]) * clut[i101 + 1] + rgbd[1] * clut[i111 + 1];
117 output[2] = (1 - rgbd[2]) * clut[i000 + 2] + (rgbd[2] - rgbd[0]) * clut[i001 + 2]
118 + (rgbd[0] - rgbd[1]) * clut[i101 + 2] + rgbd[1] * clut[i111 + 2];
119 }
120 }
121 else
122 {
123 if(rgbd[2] > rgbd[1])
124 {
125 output[0] = (1 - rgbd[2]) * clut[i000] + (rgbd[2] - rgbd[1]) * clut[i001]
126 + (rgbd[1] - rgbd[0]) * clut[i011] + rgbd[0] * clut[i111];
127 output[1] = (1 - rgbd[2]) * clut[i000 + 1] + (rgbd[2] - rgbd[1]) * clut[i001 + 1]
128 + (rgbd[1] - rgbd[0]) * clut[i011 + 1] + rgbd[0] * clut[i111 + 1];
129 output[2] = (1 - rgbd[2]) * clut[i000 + 2] + (rgbd[2] - rgbd[1]) * clut[i001 + 2]
130 + (rgbd[1] - rgbd[0]) * clut[i011 + 2] + rgbd[0] * clut[i111 + 2];
131 }
132 else if(rgbd[2] > rgbd[0])
133 {
134 output[0] = (1 - rgbd[1]) * clut[i000] + (rgbd[1] - rgbd[2]) * clut[i010]
135 + (rgbd[2] - rgbd[0]) * clut[i011] + rgbd[0] * clut[i111];
136 output[1] = (1 - rgbd[1]) * clut[i000 + 1] + (rgbd[1] - rgbd[2]) * clut[i010 + 1]
137 + (rgbd[2] - rgbd[0]) * clut[i011 + 1] + rgbd[0] * clut[i111 + 1];
138 output[2] = (1 - rgbd[1]) * clut[i000 + 2] + (rgbd[1] - rgbd[2]) * clut[i010 + 2]
139 + (rgbd[2] - rgbd[0]) * clut[i011 + 2] + rgbd[0] * clut[i111 + 2];
140 }
141 else
142 {
143 output[0] = (1 - rgbd[1]) * clut[i000] + (rgbd[1] - rgbd[0]) * clut[i010]
144 + (rgbd[0] - rgbd[2]) * clut[i110] + rgbd[2] * clut[i111];
145 output[1] = (1 - rgbd[1]) * clut[i000 + 1] + (rgbd[1] - rgbd[0]) * clut[i010 + 1]
146 + (rgbd[0] - rgbd[2]) * clut[i110 + 1] + rgbd[2] * clut[i111 + 1];
147 output[2] = (1 - rgbd[1]) * clut[i000 + 2] + (rgbd[1] - rgbd[0]) * clut[i010 + 2]
148 + (rgbd[0] - rgbd[2]) * clut[i110 + 2] + rgbd[2] * clut[i111 + 2];
149 }
150 }
151
153 }
154}
155
157void dt_lut3d_trilinear_interp(const float *const in, float *const out, const size_t pixel_nb,
158 const float *const restrict clut, const uint16_t level,
159 const float normalization)
160{
161 const int level2 = level * level;
162 const float safe_normalization = fmaxf(normalization, 1e-6f);
164 for(size_t k = 0; k < (size_t)(pixel_nb * 4); k += 4)
165 {
166 const float *const input = in + k;
167 float *const output = out + k;
168 float normalized[3];
169 float residual[3];
170 float rgbd[3];
171 int rgbi[3];
172 float tmp[6];
173
175
176 const int color = rgbi[0] + rgbi[1] * level + rgbi[2] * level2;
177 int i = color * 3;
178 int j = (color + 1) * 3;
179
180 tmp[0] = clut[i] * (1 - rgbd[0]) + clut[j] * rgbd[0];
181 tmp[1] = clut[i + 1] * (1 - rgbd[0]) + clut[j + 1] * rgbd[0];
182 tmp[2] = clut[i + 2] * (1 - rgbd[0]) + clut[j + 2] * rgbd[0];
183
184 i = (color + level) * 3;
185 j = (color + level + 1) * 3;
186
187 tmp[3] = clut[i] * (1 - rgbd[0]) + clut[j] * rgbd[0];
188 tmp[4] = clut[i + 1] * (1 - rgbd[0]) + clut[j + 1] * rgbd[0];
189 tmp[5] = clut[i + 2] * (1 - rgbd[0]) + clut[j + 2] * rgbd[0];
190
191 output[0] = tmp[0] * (1 - rgbd[1]) + tmp[3] * rgbd[1];
192 output[1] = tmp[1] * (1 - rgbd[1]) + tmp[4] * rgbd[1];
193 output[2] = tmp[2] * (1 - rgbd[1]) + tmp[5] * rgbd[1];
194
195 i = (color + level2) * 3;
196 j = (color + level2 + 1) * 3;
197
198 tmp[0] = clut[i] * (1 - rgbd[0]) + clut[j] * rgbd[0];
199 tmp[1] = clut[i + 1] * (1 - rgbd[0]) + clut[j + 1] * rgbd[0];
200 tmp[2] = clut[i + 2] * (1 - rgbd[0]) + clut[j + 2] * rgbd[0];
201
202 i = (color + level + level2) * 3;
203 j = (color + level + level2 + 1) * 3;
204
205 tmp[3] = clut[i] * (1 - rgbd[0]) + clut[j] * rgbd[0];
206 tmp[4] = clut[i + 1] * (1 - rgbd[0]) + clut[j + 1] * rgbd[0];
207 tmp[5] = clut[i + 2] * (1 - rgbd[0]) + clut[j + 2] * rgbd[0];
208
209 tmp[0] = tmp[0] * (1 - rgbd[1]) + tmp[3] * rgbd[1];
210 tmp[1] = tmp[1] * (1 - rgbd[1]) + tmp[4] * rgbd[1];
211 tmp[2] = tmp[2] * (1 - rgbd[1]) + tmp[5] * rgbd[1];
212
213 output[0] = output[0] * (1 - rgbd[2]) + tmp[0] * rgbd[2];
214 output[1] = output[1] * (1 - rgbd[2]) + tmp[1] * rgbd[2];
215 output[2] = output[2] * (1 - rgbd[2]) + tmp[2] * rgbd[2];
216
218 }
219}
220
222void dt_lut3d_pyramid_interp(const float *const in, float *const out, const size_t pixel_nb,
223 const float *const restrict clut, const uint16_t level,
224 const float normalization)
225{
226 const int level2 = level * level;
227 const float safe_normalization = fmaxf(normalization, 1e-6f);
229 for(size_t k = 0; k < (size_t)(pixel_nb * 4); k += 4)
230 {
231 const float *const input = in + k;
232 float *const output = out + k;
233 float normalized[3];
234 float residual[3];
235 float rgbd[3];
236 int rgbi[3];
237
239
240 const int color = rgbi[0] + rgbi[1] * level + rgbi[2] * level2;
241 const int i000 = color * 3;
242 const int i100 = i000 + 3;
243 const int i010 = (color + level) * 3;
244 const int i110 = i010 + 3;
245 const int i001 = (color + level2) * 3;
246 const int i101 = i001 + 3;
247 const int i011 = (color + level + level2) * 3;
248 const int i111 = i011 + 3;
249
250 if(rgbd[1] > rgbd[0] && rgbd[2] > rgbd[0])
251 {
252 output[0] = clut[i000] + (clut[i111] - clut[i011]) * rgbd[0] + (clut[i010] - clut[i000]) * rgbd[1]
253 + (clut[i001] - clut[i000]) * rgbd[2]
254 + (clut[i011] - clut[i001] - clut[i010] + clut[i000]) * rgbd[1] * rgbd[2];
255 output[1] = clut[i000 + 1] + (clut[i111 + 1] - clut[i011 + 1]) * rgbd[0]
256 + (clut[i010 + 1] - clut[i000 + 1]) * rgbd[1]
257 + (clut[i001 + 1] - clut[i000 + 1]) * rgbd[2]
258 + (clut[i011 + 1] - clut[i001 + 1] - clut[i010 + 1] + clut[i000 + 1]) * rgbd[1] * rgbd[2];
259 output[2] = clut[i000 + 2] + (clut[i111 + 2] - clut[i011 + 2]) * rgbd[0]
260 + (clut[i010 + 2] - clut[i000 + 2]) * rgbd[1]
261 + (clut[i001 + 2] - clut[i000 + 2]) * rgbd[2]
262 + (clut[i011 + 2] - clut[i001 + 2] - clut[i010 + 2] + clut[i000 + 2]) * rgbd[1] * rgbd[2];
263 }
264 else if(rgbd[0] > rgbd[1] && rgbd[2] > rgbd[1])
265 {
266 output[0] = clut[i000] + (clut[i100] - clut[i000]) * rgbd[0] + (clut[i111] - clut[i101]) * rgbd[1]
267 + (clut[i001] - clut[i000]) * rgbd[2]
268 + (clut[i101] - clut[i001] - clut[i100] + clut[i000]) * rgbd[0] * rgbd[2];
269 output[1] = clut[i000 + 1] + (clut[i100 + 1] - clut[i000 + 1]) * rgbd[0]
270 + (clut[i111 + 1] - clut[i101 + 1]) * rgbd[1]
271 + (clut[i001 + 1] - clut[i000 + 1]) * rgbd[2]
272 + (clut[i101 + 1] - clut[i001 + 1] - clut[i100 + 1] + clut[i000 + 1]) * rgbd[0] * rgbd[2];
273 output[2] = clut[i000 + 2] + (clut[i100 + 2] - clut[i000 + 2]) * rgbd[0]
274 + (clut[i111 + 2] - clut[i101 + 2]) * rgbd[1]
275 + (clut[i001 + 2] - clut[i000 + 2]) * rgbd[2]
276 + (clut[i101 + 2] - clut[i001 + 2] - clut[i100 + 2] + clut[i000 + 2]) * rgbd[0] * rgbd[2];
277 }
278 else
279 {
280 output[0] = clut[i000] + (clut[i100] - clut[i000]) * rgbd[0] + (clut[i010] - clut[i000]) * rgbd[1]
281 + (clut[i111] - clut[i110]) * rgbd[2]
282 + (clut[i110] - clut[i100] - clut[i010] + clut[i000]) * rgbd[0] * rgbd[1];
283 output[1] = clut[i000 + 1] + (clut[i100 + 1] - clut[i000 + 1]) * rgbd[0]
284 + (clut[i010 + 1] - clut[i000 + 1]) * rgbd[1]
285 + (clut[i111 + 1] - clut[i110 + 1]) * rgbd[2]
286 + (clut[i110 + 1] - clut[i100 + 1] - clut[i010 + 1] + clut[i000 + 1]) * rgbd[0] * rgbd[1];
287 output[2] = clut[i000 + 2] + (clut[i100 + 2] - clut[i000 + 2]) * rgbd[0]
288 + (clut[i010 + 2] - clut[i000 + 2]) * rgbd[1]
289 + (clut[i111 + 2] - clut[i110 + 2]) * rgbd[2]
290 + (clut[i110 + 2] - clut[i100 + 2] - clut[i010 + 2] + clut[i000 + 2]) * rgbd[0] * rgbd[1];
291 }
292
294 }
295}
296
297void dt_lut3d_apply(const float *const in, float *const out, const size_t pixel_nb, const float *const clut,
298 const uint16_t level, const float normalization,
299 const dt_lut3d_interpolation_t interpolation)
300{
301 switch(interpolation)
302 {
305 return;
308 return;
310 default:
312 return;
313 }
314}
const dt_colormatrix_t dt_aligned_pixel_t out
GdkRGBA color[]
Definition geotagging.c:539
float *const restrict const size_t k
dt_lut3d_interpolation_t
Definition lut3d.h:26
@ DT_LUT3D_INTERP_PYRAMID
Definition lut3d.h:29
@ DT_LUT3D_INTERP_TETRAHEDRAL
Definition lut3d.h:27
@ DT_LUT3D_INTERP_TRILINEAR
Definition lut3d.h:28
#define __OMP_PARALLEL_FOR__(...)
Definition openmp.h:60
__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)
Definition pixel/lut3d.c:63
__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)
Definition pixel/lut3d.c:25
float dt_aligned_pixel_simd_t __attribute__((vector_size(16), aligned(16)))
Apply one channel's tone curve to each of the three colour channels, or pass the channel through unto...
Definition simd.h:55
#define __DT_CLONE_TARGETS__