Ansel 0.0
A darktable fork - bloat + design vision
Loading...
Searching...
No Matches
colorequal_shared.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
20
22#include "common/curve_tools.h"
25#include "common/splines.h"
26
27#include <float.h>
28#include <math.h>
29#include <string.h>
30
32 const dt_iop_order_iccprofile_info_t *profile,
34{
35 dt_aligned_pixel_t linear_rgb = { 0.f };
37
38 if(profile->nonlinearlut)
39 _apply_trc(linear_rgb, RGB, profile->lut_out, profile->unbounded_coeffs_out, profile->lutsize);
40 else
41 for_each_channel(c, aligned(RGB, linear_rgb)) RGB[c] = linear_rgb[c];
42}
43
45 const dt_iop_order_iccprofile_info_t *profile,
47{
48 if(profile)
50 else
51 dt_XYZ_to_linearRGB(XYZ_D50, RGB);
52}
53
54static inline void _dt_ucs_hsb_to_preview_rgb_unclamped(const dt_aligned_pixel_t HSB, const float white,
56{
59 dt_UCS_HSB_to_XYZ(HSB, white, XYZ_D65);
62}
63
65 const dt_iop_order_iccprofile_info_t *display_profile,
67{
68 dt_aligned_pixel_t linear_rgb = { linear_rgb_in[0], linear_rgb_in[1], linear_rgb_in[2], 0.f };
69
77 const float max_rgb = fmaxf(linear_rgb[0], fmaxf(linear_rgb[1], linear_rgb[2]));
78 if(linear_rgb[0] > 1.f || linear_rgb[1] > 1.f || linear_rgb[2] > 1.f)
79 for_each_channel(c, aligned(linear_rgb)) linear_rgb[c] /= max_rgb;
80
81 if(display_profile && display_profile->nonlinearlut)
82 _apply_trc(linear_rgb, RGB, display_profile->lut_out, display_profile->unbounded_coeffs_out,
83 display_profile->lutsize);
84 else if(display_profile)
85 for_each_channel(c, aligned(RGB, linear_rgb)) RGB[c] = linear_rgb[c];
86 else
87 for_each_channel(c, aligned(RGB, linear_rgb))
88 RGB[c] = linear_rgb[c] <= 0.0031308f ? 12.92f * linear_rgb[c]
89 : (1.0f + 0.055f) * powf(linear_rgb[c], 1.0f / 2.4f) - 0.055f;
90}
91
92static inline void _dt_ucs_hsb_to_display_rgb_normalized(const dt_aligned_pixel_t HSB, const float white,
93 const dt_iop_order_iccprofile_info_t *display_profile,
95{
98 dt_aligned_pixel_t linear_rgb = { 0.f };
99 dt_UCS_HSB_to_XYZ(HSB, white, XYZ_D65);
101 _xyz_d50_to_profile_linear_rgb(XYZ_D50, display_profile, linear_rgb);
102 _profile_linear_rgb_to_display_rgb_normalized(linear_rgb, display_profile, RGB);
103}
104
106{
107 return Y_to_dt_UCS_L_star(1.f);
108}
109
111{
112 while(hue < 0.f) hue += 2.f * M_PI_F;
113 while(hue >= 2.f * M_PI_F) hue -= 2.f * M_PI_F;
114 return hue;
115}
116
118{
120 return hue - M_PI_F;
121}
122
124{
125 return dt_colorrings_wrap_hue_pi((360.f * x + DT_COLORRINGS_ANGLE_SHIFT) * M_PI_F / 180.f);
126}
127
128float dt_colorrings_hue_to_curve_x(const float hue)
129{
130 return dt_colorrings_wrap_hue_2pi(hue - DT_COLORRINGS_ANGLE_SHIFT * M_PI_F / 180.f) / (2.f * M_PI_F);
131}
132
133float dt_colorrings_curve_periodic_distance(const float x0, const float x1)
134{
135 const float distance = fabsf(x0 - x1);
136 return fminf(distance, 1.f - distance);
137}
138
140{
141 switch(ring)
142 {
144 return 0.15f;
146 return 0.75f;
148 default:
149 return 0.45f;
150 }
151}
152
153float dt_colorrings_curve_periodic_sample(const dt_colorrings_node_t *curve, const int nodes, const float x)
154{
162 if(IS_NULL_PTR(curve) || nodes < 2) return 0.5f;
163
165
166 for(int k = 0; k < nodes; k++)
167 {
168 anchors[k].x = curve[k].x;
169 anchors[k].y = curve[k].y;
170 }
171
172 return interpolate_val_V2_periodic(nodes, anchors, x, MONOTONE_HERMITE, 1.f);
173}
174
175gboolean dt_colorrings_apply_rgb_lut(const dt_aligned_pixel_t input_rgb, const float white_level,
176 const dt_iop_order_iccprofile_info_t *work_profile,
177 const dt_iop_order_iccprofile_info_t *lut_profile, const float *clut,
178 const uint16_t clut_level, dt_pthread_rwlock_t *clut_lock,
179 const dt_lut3d_interpolation_t interpolation, dt_aligned_pixel_t output_rgb)
180{
181 if(IS_NULL_PTR(output_rgb)) return FALSE;
182
183 memcpy(output_rgb, input_rgb, sizeof(dt_aligned_pixel_t));
184
185 if(IS_NULL_PTR(work_profile) || IS_NULL_PTR(lut_profile) || IS_NULL_PTR(clut) || clut_level == 0) return FALSE;
186
187 const float normalized_white = fmaxf(white_level, 1e-6f);
188 output_rgb[0] = input_rgb[0] / normalized_white;
189 output_rgb[1] = input_rgb[1] / normalized_white;
190 output_rgb[2] = input_rgb[2] / normalized_white;
191 output_rgb[3] = 0.f;
192
193 dt_ioppr_transform_image_colorspace_rgb((float *)output_rgb, (float *)output_rgb, 1, 1, work_profile, lut_profile,
194 "colorrings swatch work to HLG Rec2020");
195 if(clut_lock) dt_pthread_rwlock_rdlock(clut_lock);
196 dt_lut3d_apply((float *)output_rgb, (float *)output_rgb, 1, clut, clut_level, 1.f, interpolation);
197 if(clut_lock) dt_pthread_rwlock_unlock(clut_lock);
198 dt_ioppr_transform_image_colorspace_rgb((float *)output_rgb, (float *)output_rgb, 1, 1, lut_profile, work_profile,
199 "colorrings swatch HLG Rec2020 to work");
200
201 output_rgb[0] *= normalized_white;
202 output_rgb[1] *= normalized_white;
203 output_rgb[2] *= normalized_white;
204 output_rgb[3] = 0.f;
205 return TRUE;
206}
207
208void dt_colorrings_hsb_to_profile_rgb(const dt_aligned_pixel_t HSB, const float white,
210{
211 dt_aligned_pixel_t XYZ_D65 = { 0.f };
212 dt_aligned_pixel_t XYZ_D50 = { 0.f };
213 dt_UCS_HSB_to_XYZ(HSB, white, XYZ_D65);
216}
217
218void dt_colorrings_hsb_to_display_rgb(const dt_aligned_pixel_t HSB, const float white,
220{
221 _dt_ucs_hsb_to_display_rgb_normalized(HSB, white, display_profile, RGB);
222 for_each_channel(c, aligned(RGB)) RGB[c] = CLAMP(RGB[c], 0.f, 1.f);
223}
224
226 const dt_iop_order_iccprofile_info_t *profile,
227 const dt_iop_order_iccprofile_info_t *display_profile,
228 dt_aligned_pixel_t display_rgb)
229{
230 dt_aligned_pixel_t XYZ_D50 = { 0.f };
231 dt_aligned_pixel_t linear_rgb = { 0.f };
232
233 if(profile)
234 dt_ioppr_rgb_matrix_to_xyz(RGB, XYZ_D50, profile->matrix_in_transposed, profile->lut_in,
235 profile->unbounded_coeffs_in, profile->lutsize, profile->nonlinearlut);
236 else
237 dt_linearRGB_to_XYZ(RGB, XYZ_D50);
238
239 _xyz_d50_to_profile_linear_rgb(XYZ_D50, display_profile, linear_rgb);
240 _profile_linear_rgb_to_display_rgb_normalized(linear_rgb, display_profile, display_rgb);
241 for_each_channel(c, aligned(display_rgb)) display_rgb[c] = CLAMP(display_rgb[c], 0.f, 1.f);
242}
243
246{
247 dt_aligned_pixel_t XYZ_D50 = { 0.f };
248 dt_aligned_pixel_t XYZ_D65 = { 0.f };
249 dt_aligned_pixel_t xyY = { 0.f };
250
251 if(IS_NULL_PTR(profile))
252 {
253 memset(JCH, 0, sizeof(dt_aligned_pixel_t));
254 return;
255 }
256
257 dt_ioppr_rgb_matrix_to_xyz(RGB, XYZ_D50, profile->matrix_in_transposed, profile->lut_in,
258 profile->unbounded_coeffs_in, profile->lutsize, profile->nonlinearlut);
260 for_each_channel(c, aligned(XYZ_D65)) XYZ_D65[c] = fmaxf(XYZ_D65[c], 0.f);
261
262 if(XYZ_D65[0] + XYZ_D65[1] + XYZ_D65[2] <= 1e-6f)
263 {
264 memset(JCH, 0, sizeof(dt_aligned_pixel_t));
265 return;
266 }
267
269 xyY_to_dt_UCS_JCH(xyY, white, JCH);
270}
271
274{
275 dt_aligned_pixel_t JCH = { 0.f };
276 dt_colorrings_profile_rgb_to_dt_ucs_jch(RGB, white, profile, JCH);
277 dt_UCS_JCH_to_HSB(JCH, HSB);
278}
279
282{
283 dt_aligned_pixel_t XYZ_D50 = { 0.f };
284 dt_aligned_pixel_t XYZ_D65 = { 0.f };
285
286 if(IS_NULL_PTR(profile))
287 {
288 memset(Ych, 0, sizeof(dt_aligned_pixel_t));
289 return;
290 }
291
292 dt_ioppr_rgb_matrix_to_xyz(RGB, XYZ_D50, profile->matrix_in_transposed, profile->lut_in,
293 profile->unbounded_coeffs_in, profile->lutsize, profile->nonlinearlut);
295 XYZ_to_Ych(XYZ_D65, Ych);
296
297 if(Ych[2] < 0.f) Ych[2] = 2.f * M_PI_F + Ych[2];
298}
299
300static float _compute_reference_saturation(const float white, const float brightness)
301{
302 float low = 0.f;
303 float high = 1.f;
304
310 for(int iter = 0; iter < 18; iter++)
311 {
312 const float candidate = 0.5f * (low + high);
313 gboolean valid = TRUE;
314
315 for(int hue = 0; hue < DT_COLORRINGS_HUE_SAMPLES; hue++)
316 {
317 dt_aligned_pixel_t RGB = { 0.f };
319 candidate, brightness, 0.f };
321
322 if(RGB[0] < 0.f || RGB[0] > 1.f || RGB[1] < 0.f || RGB[1] > 1.f || RGB[2] < 0.f || RGB[2] > 1.f)
323 {
324 valid = FALSE;
325 break;
326 }
327 }
328
329 if(valid)
330 low = candidate;
331 else
332 high = candidate;
333 }
334
335 return low;
336}
337
339 float reference_saturation[DT_COLORRINGS_NUM_RINGS])
340{
341 for(int ring = 0; ring < DT_COLORRINGS_NUM_RINGS; ring++)
342 if(reference_saturation[ring] == 0.f)
343 reference_saturation[ring]
345}
346
347float dt_colorrings_ring_axis_position_from_brightness(const float brightness, const float white,
348 const dt_iop_order_iccprofile_info_t *profile)
349{
350 const dt_aligned_pixel_t HSB = { 0.f, 0.f, CLAMP(brightness, 0.f, 1.f), 0.f };
351 dt_aligned_pixel_t RGB = { 0.f };
352 dt_colorrings_hsb_to_profile_rgb(HSB, white, profile, RGB);
353 return CLAMP((RGB[0] + RGB[1] + RGB[2]) / 3.f, 0.f, 1.f);
354}
355
356void dt_colorrings_brightness_to_axis_rgb(const float brightness, const float white,
358{
359 const float axis = dt_colorrings_ring_axis_position_from_brightness(brightness, white, profile);
360 RGB[0] = axis;
361 RGB[1] = axis;
362 RGB[2] = axis;
363 RGB[3] = 0.f;
364}
365
367{
368 float distance = INFINITY;
369
370 for(int c = 0; c < 3; c++)
371 {
372 if(fabsf(direction[c]) < 1e-6f) continue;
373
374 const float bound = (direction[c] > 0.f) ? 1.f : 0.f;
375 const float candidate = (bound - axis[c]) / direction[c];
376 if(candidate > 0.f && candidate < distance) distance = candidate;
377 }
378
379 return isfinite(distance) ? distance : 0.f;
380}
381
383{
384 dt_aligned_pixel_t vector = { RGB[0] - axis[0], RGB[1] - axis[1], RGB[2] - axis[2], 0.f };
385
386 if(dt_colorrings_vector_norm3(vector) < 1e-6f) return;
387
388 const float shell_scale = dt_colorrings_distance_to_cube_shell(axis, vector);
389 if(shell_scale < 1.f)
390 {
391 RGB[0] = axis[0] + shell_scale * vector[0];
392 RGB[1] = axis[1] + shell_scale * vector[1];
393 RGB[2] = axis[2] + shell_scale * vector[2];
394 }
395
396 RGB[0] = CLAMP(RGB[0], 0.f, 1.f);
397 RGB[1] = CLAMP(RGB[1], 0.f, 1.f);
398 RGB[2] = CLAMP(RGB[2], 0.f, 1.f);
399}
400
402{
403 return sqrtf(sqf(vector[0]) + sqf(vector[1]) + sqf(vector[2]));
404}
405
407{
408 return a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
409}
410
412{
413 out[0] = a[1] * b[2] - a[2] * b[1];
414 out[1] = a[2] * b[0] - a[0] * b[2];
415 out[2] = a[0] * b[1] - a[1] * b[0];
416 out[3] = 0.f;
417}
418
420{
421 const float norm = dt_colorrings_vector_norm3(vector);
422 if(norm < 1e-6f) return;
423
424 vector[0] /= norm;
425 vector[1] /= norm;
426 vector[2] /= norm;
427}
428
430 const float cos_angle, const float sin_angle, dt_aligned_pixel_t output)
431{
432 dt_aligned_pixel_t cross = { 0.f };
433 dt_colorrings_cross3(axis, input, cross);
434 const float axis_dot = dt_colorrings_dot3(axis, input);
435
436 for(int c = 0; c < 3; c++)
437 output[c] = input[c] * cos_angle + cross[c] * sin_angle + axis[c] * axis_dot * (1.f - cos_angle);
438 output[3] = 0.f;
439}
440
441void dt_colorrings_rgb_to_gray_cyl(const float rgb[3], float *L, float *rho, float *theta)
442{
443 const float eL0 = 0.5773502691896258f;
444 const float eL1 = 0.5773502691896258f;
445 const float eL2 = 0.5773502691896258f;
446
447 const float eu0 = 0.7071067811865475f;
448 const float eu1 = -0.7071067811865475f;
449 const float eu2 = 0.0f;
450
451 const float ev0 = 0.4082482904638630f;
452 const float ev1 = 0.4082482904638630f;
453 const float ev2 = -0.8164965809277260f;
454
455 *L = rgb[0] * eL0 + rgb[1] * eL1 + rgb[2] * eL2;
456
457 const float u = rgb[0] * eu0 + rgb[1] * eu1 + rgb[2] * eu2;
458 const float v = rgb[0] * ev0 + rgb[1] * ev1 + rgb[2] * ev2;
459
460 *rho = sqrtf(u * u + v * v);
461 *theta = atan2f(v, u);
462}
463
464void dt_colorrings_gray_basis_to_rgb(const float L, const float u, const float v, float rgb[3])
465{
466 const float eL0 = 0.5773502691896258f;
467 const float eL1 = 0.5773502691896258f;
468 const float eL2 = 0.5773502691896258f;
469
470 const float eu0 = 0.7071067811865475f;
471 const float eu1 = -0.7071067811865475f;
472 const float eu2 = 0.0f;
473
474 const float ev0 = 0.4082482904638630f;
475 const float ev1 = 0.4082482904638630f;
476 const float ev2 = -0.8164965809277260f;
477
478 rgb[0] = L * eL0 + u * eu0 + v * ev0;
479 rgb[1] = L * eL1 + u * eu1 + v * ev1;
480 rgb[2] = L * eL2 + u * eu2 + v * ev2;
481}
482
484{
485 const float value = L * 0.5773502691896258f;
486 RGB[0] = value;
487 RGB[1] = value;
488 RGB[2] = value;
489 RGB[3] = 0.f;
490}
491
493{
494 if(d >= 1.0f) return 0.0f;
495 const float t = 1.0f - d;
496 return t * t * t * t * (4.0f * d + 1.0f);
497}
498
500{
501 const float two_pi = 2.0f * (float)M_PI;
502 while(x <= -(float)M_PI) x += two_pi;
503 while(x > (float)M_PI) x -= two_pi;
504 return x;
505}
506
508 const float x[3], const float anchor_L[DT_COLORRINGS_LOCAL_FIELD_RINGS][DT_COLORRINGS_HUE_SAMPLES],
513 const float delta_theta[DT_COLORRINGS_LOCAL_FIELD_RINGS][DT_COLORRINGS_HUE_SAMPLES], const float inv_sigma_L,
514 const float inv_sigma_rho, const float inv_sigma_theta, const float rho0, float out[3])
515{
516 float Lx, rhox, thetax;
517 dt_colorrings_rgb_to_gray_cyl(x, &Lx, &rhox, &thetax);
518
519 if(rhox <= 1e-6f)
520 {
521 out[0] = 0.f;
522 out[1] = 0.f;
523 out[2] = 0.f;
524 return;
525 }
526
527 float sum_w = 0.f;
528 float sum_dL = 0.f;
529 float sum_scale = 0.f;
530 float sum_dtheta = 0.f;
531
533 const int axis_ring = DT_COLORRINGS_LOCAL_FIELD_RINGS - 1;
534 const float axis_weight_scale = 1.0f / (float)DT_COLORRINGS_HUE_SAMPLES;
535
540 for(int k = 0; k < n; k++)
541 {
542 const int ring = k / DT_COLORRINGS_HUE_SAMPLES;
543 const int h = k - ring * DT_COLORRINGS_HUE_SAMPLES;
544 const float dL = (Lx - anchor_L[ring][h]) * inv_sigma_L;
545 const float dr = (rhox - anchor_rho[ring][h]) * inv_sigma_rho;
546 const float dh = dt_colorrings_wrap_pi(thetax - anchor_theta[ring][h]) * inv_sigma_theta;
547 const float d2 = dL * dL + dr * dr + dh * dh;
548
549 if(d2 >= 1.f) continue;
550
551 float w = dt_colorrings_wendland_c2(sqrtf(d2));
552 if(ring == axis_ring) w *= axis_weight_scale;
553
554 sum_w += w;
555 sum_dL += w * delta_L[ring][h];
556 sum_scale += w * chroma_scale[ring][h];
557 sum_dtheta += w * delta_theta[ring][h];
558 }
559
560 if(sum_w <= FLT_MIN)
561 {
562 out[0] = 0.f;
563 out[1] = 0.f;
564 out[2] = 0.f;
565 return;
566 }
567
568 const float inv_w = 1.0f / sum_w;
569 const float target_delta_L = sum_dL * inv_w;
570 const float scale = sum_scale * inv_w;
571 const float target_delta_theta = sum_dtheta * inv_w;
572 if(fabsf(target_delta_L) <= 1e-6f && fabsf(scale - 1.f) <= 1e-6f && fabsf(target_delta_theta) <= 1e-6f)
573 {
574 out[0] = 0.f;
575 out[1] = 0.f;
576 out[2] = 0.f;
577 return;
578 }
579 const float t = CLAMP(rhox / rho0, 0.f, 1.f);
580 const float alpha = t * t * (3.0f - 2.0f * t);
581 const float target_L = Lx + alpha * target_delta_L;
582 const float target_rho = rhox * fmaxf(1.f + alpha * (scale - 1.f), 0.f);
583 const float target_theta = thetax + alpha * target_delta_theta;
584 dt_aligned_pixel_t target_rgb = { 0.f };
585 dt_aligned_pixel_t axis = { 0.f };
586 dt_colorrings_gray_basis_to_rgb(target_L, target_rho * cosf(target_theta), target_rho * sinf(target_theta),
587 target_rgb);
589 dt_colorrings_project_to_cube_shell(axis, target_rgb);
590
591 out[0] = target_rgb[0] - x[0];
592 out[1] = target_rgb[1] - x[1];
593 out[2] = target_rgb[2] - x[2];
594}
595
597 float *lut, const int level, const float anchor_L[DT_COLORRINGS_LOCAL_FIELD_RINGS][DT_COLORRINGS_HUE_SAMPLES],
602 const float delta_theta[DT_COLORRINGS_LOCAL_FIELD_RINGS][DT_COLORRINGS_HUE_SAMPLES], const float inv_sigma_L,
603 const float inv_sigma_rho, const float inv_sigma_theta, const float rho0)
604{
605 __OMP_PARALLEL_FOR__(collapse(3))
606 for(int b = 0; b < level; b++)
607 for(int g = 0; g < level; g++)
608 for(int r = 0; r < level; r++)
609 {
610 const float x[3]
611 = { (float)r / (float)(level - 1), (float)g / (float)(level - 1), (float)b / (float)(level - 1) };
612 float d[3];
613 dt_colorrings_eval_local_field(x, anchor_L, anchor_rho, anchor_theta, delta_L, chroma_scale, delta_theta,
614 inv_sigma_L, inv_sigma_rho, inv_sigma_theta, rho0, d);
615
616 const size_t idx = (((size_t)b * level + (size_t)g) * level + (size_t)r) * 3u;
617 lut[idx + 0] = CLAMP(x[0] + d[0], 0.f, 1.f);
618 lut[idx + 1] = CLAMP(x[1] + d[1], 0.f, 1.f);
619 lut[idx + 2] = CLAMP(x[2] + d[2], 0.f, 1.f);
620 }
621}
622
624 const int anchor_count, const float inv_sigma_L,
625 const float inv_sigma_rho, const float inv_sigma_theta,
626 const float rho0, float out[3])
627{
628 float Lx, rhox, thetax;
629 dt_colorrings_rgb_to_gray_cyl(x, &Lx, &rhox, &thetax);
630
631 if(rhox <= 1e-6f || IS_NULL_PTR(anchors) || anchor_count <= 0)
632 {
633 out[0] = 0.f;
634 out[1] = 0.f;
635 out[2] = 0.f;
636 return;
637 }
638
639 float sum_w = 0.f;
640 float sum_dL = 0.f;
641 float sum_scale = 0.f;
642 float sum_dtheta = 0.f;
643
650 for(int k = 0; k < anchor_count; k++)
651 {
652 const float dL = (Lx - anchors[k].L) * inv_sigma_L;
653 const float dr = (rhox - anchors[k].rho) * inv_sigma_rho;
654 const float dh = dt_colorrings_wrap_pi(thetax - anchors[k].theta) * inv_sigma_theta;
655 const float d2 = dL * dL + dr * dr + dh * dh;
656
657 if(d2 >= 1.f) continue;
658
659 const float w = anchors[k].weight * dt_colorrings_wendland_c2(sqrtf(d2));
660 if(w <= FLT_MIN) continue;
661
662 sum_w += w;
663 sum_dL += w * anchors[k].delta_L;
664 sum_scale += w * anchors[k].chroma_scale;
665 sum_dtheta += w * anchors[k].delta_theta;
666 }
667
668 if(sum_w <= FLT_MIN)
669 {
670 out[0] = 0.f;
671 out[1] = 0.f;
672 out[2] = 0.f;
673 return;
674 }
675
676 const float inv_w = 1.0f / sum_w;
677 const float target_delta_L = sum_dL * inv_w;
678 const float scale = sum_scale * inv_w;
679 const float target_delta_theta = sum_dtheta * inv_w;
680 if(fabsf(target_delta_L) <= 1e-6f && fabsf(scale - 1.f) <= 1e-6f && fabsf(target_delta_theta) <= 1e-6f)
681 {
682 out[0] = 0.f;
683 out[1] = 0.f;
684 out[2] = 0.f;
685 return;
686 }
687 const float t = CLAMP(rhox / rho0, 0.f, 1.f);
688 const float alpha = t * t * (3.0f - 2.0f * t);
689 const float target_L = Lx + alpha * target_delta_L;
690 const float target_rho = rhox * fmaxf(1.f + alpha * (scale - 1.f), 0.f);
691 const float target_theta = thetax + alpha * target_delta_theta;
692 dt_aligned_pixel_t target_rgb = { 0.f };
693 dt_aligned_pixel_t axis = { 0.f };
694 dt_colorrings_gray_basis_to_rgb(target_L, target_rho * cosf(target_theta), target_rho * sinf(target_theta),
695 target_rgb);
697 dt_colorrings_project_to_cube_shell(axis, target_rgb);
698
699 out[0] = target_rgb[0] - x[0];
700 out[1] = target_rgb[1] - x[1];
701 out[2] = target_rgb[2] - x[2];
702}
703
705 const dt_colorrings_sparse_anchor_t *const anchors,
706 const int anchor_count, const float inv_sigma_L,
707 const float inv_sigma_rho, const float inv_sigma_theta,
708 const float rho0)
709{
710 __OMP_PARALLEL_FOR__(collapse(3))
711 for(int b = 0; b < level; b++)
712 for(int g = 0; g < level; g++)
713 for(int r = 0; r < level; r++)
714 {
715 const float x[3]
716 = { (float)r / (float)(level - 1), (float)g / (float)(level - 1), (float)b / (float)(level - 1) };
717 float d[3] = { 0.f };
718 if(anchors && anchor_count > 0)
719 dt_colorrings_eval_sparse_local_field(x, anchors, anchor_count, inv_sigma_L, inv_sigma_rho, inv_sigma_theta,
720 rho0, d);
721
722 const size_t idx = (((size_t)b * level + (size_t)g) * level + (size_t)r) * 3u;
723 lut[idx + 0] = CLAMP(x[0] + d[0], 0.f, 1.f);
724 lut[idx + 1] = CLAMP(x[1] + d[1], 0.f, 1.f);
725 lut[idx + 2] = CLAMP(x[2] + d[2], 0.f, 1.f);
726 }
727}
#define TRUE
Definition ashift_lsd.c:162
#define FALSE
Definition ashift_lsd.c:158
static void XYZ_D50_to_D65(const dt_aligned_pixel_t XYZ_in, dt_aligned_pixel_t XYZ_out)
static void XYZ_D65_to_D50(const dt_aligned_pixel_t XYZ_in, dt_aligned_pixel_t XYZ_out)
void dt_colorrings_profile_rgb_to_dt_ucs_hsb(const dt_aligned_pixel_t RGB, const float white, const dt_iop_order_iccprofile_info_t *profile, dt_aligned_pixel_t HSB)
void dt_colorrings_eval_sparse_local_field(const float x[3], const dt_colorrings_sparse_anchor_t *const anchors, const int anchor_count, const float inv_sigma_L, const float inv_sigma_rho, const float inv_sigma_theta, const float rho0, float out[3])
float dt_colorrings_wendland_c2(float d)
void dt_colorrings_profile_rgb_to_display_rgb(const dt_aligned_pixel_t RGB, const dt_iop_order_iccprofile_info_t *profile, const dt_iop_order_iccprofile_info_t *display_profile, dt_aligned_pixel_t display_rgb)
void dt_colorrings_fill_lut_sparse_local_field(float *lut, const int level, const dt_colorrings_sparse_anchor_t *const anchors, const int anchor_count, const float inv_sigma_L, const float inv_sigma_rho, const float inv_sigma_theta, const float rho0)
void dt_colorrings_hsb_to_profile_rgb(const dt_aligned_pixel_t HSB, const float white, const dt_iop_order_iccprofile_info_t *profile, dt_aligned_pixel_t RGB)
static void _profile_linear_rgb_to_display_rgb_normalized(const dt_aligned_pixel_t linear_rgb_in, const dt_iop_order_iccprofile_info_t *display_profile, dt_aligned_pixel_t RGB)
void dt_colorrings_gray_basis_to_rgb(const float L, const float u, const float v, float rgb[3])
float dt_colorrings_distance_to_cube_shell(const dt_aligned_pixel_t axis, const dt_aligned_pixel_t direction)
float dt_colorrings_curve_periodic_sample(const dt_colorrings_node_t *curve, const int nodes, const float x)
float dt_colorrings_curve_periodic_distance(const float x0, const float x1)
void dt_colorrings_gray_axis_rgb_from_L(const float L, dt_aligned_pixel_t RGB)
void dt_colorrings_rgb_to_gray_cyl(const float rgb[3], float *L, float *rho, float *theta)
void dt_colorrings_eval_local_field(const float x[3], const float anchor_L[DT_COLORRINGS_LOCAL_FIELD_RINGS][DT_COLORRINGS_HUE_SAMPLES], const float anchor_rho[DT_COLORRINGS_LOCAL_FIELD_RINGS][DT_COLORRINGS_HUE_SAMPLES], const float anchor_theta[DT_COLORRINGS_LOCAL_FIELD_RINGS][DT_COLORRINGS_HUE_SAMPLES], const float delta_L[DT_COLORRINGS_LOCAL_FIELD_RINGS][DT_COLORRINGS_HUE_SAMPLES], const float chroma_scale[DT_COLORRINGS_LOCAL_FIELD_RINGS][DT_COLORRINGS_HUE_SAMPLES], const float delta_theta[DT_COLORRINGS_LOCAL_FIELD_RINGS][DT_COLORRINGS_HUE_SAMPLES], const float inv_sigma_L, const float inv_sigma_rho, const float inv_sigma_theta, const float rho0, float out[3])
float dt_colorrings_wrap_hue_pi(float hue)
gboolean dt_colorrings_apply_rgb_lut(const dt_aligned_pixel_t input_rgb, const float white_level, const dt_iop_order_iccprofile_info_t *work_profile, const dt_iop_order_iccprofile_info_t *lut_profile, const float *clut, const uint16_t clut_level, dt_pthread_rwlock_t *clut_lock, const dt_lut3d_interpolation_t interpolation, dt_aligned_pixel_t output_rgb)
float dt_colorrings_ring_brightness(const dt_colorrings_ring_t ring)
void dt_colorrings_compute_reference_saturations(const float white, float reference_saturation[DT_COLORRINGS_NUM_RINGS])
float dt_colorrings_wrap_hue_2pi(float hue)
void dt_colorrings_normalize3(dt_aligned_pixel_t vector)
void dt_colorrings_project_to_cube_shell(const dt_aligned_pixel_t axis, dt_aligned_pixel_t RGB)
float dt_colorrings_hue_to_curve_x(const float hue)
void dt_colorrings_rotate_around_axis(const dt_aligned_pixel_t input, const dt_aligned_pixel_t axis, const float cos_angle, const float sin_angle, dt_aligned_pixel_t output)
void dt_colorrings_hsb_to_display_rgb(const dt_aligned_pixel_t HSB, const float white, const dt_iop_order_iccprofile_info_t *display_profile, dt_aligned_pixel_t RGB)
void dt_colorrings_cross3(const dt_aligned_pixel_t a, const dt_aligned_pixel_t b, dt_aligned_pixel_t out)
float dt_colorrings_wrap_pi(float x)
static float _compute_reference_saturation(const float white, const float brightness)
void dt_colorrings_profile_rgb_to_dt_ucs_jch(const dt_aligned_pixel_t RGB, const float white, const dt_iop_order_iccprofile_info_t *profile, dt_aligned_pixel_t JCH)
float dt_colorrings_vector_norm3(const dt_aligned_pixel_t vector)
void dt_colorrings_profile_rgb_to_Ych(const dt_aligned_pixel_t RGB, const dt_iop_order_iccprofile_info_t *profile, dt_aligned_pixel_t Ych)
float dt_colorrings_ring_axis_position_from_brightness(const float brightness, const float white, const dt_iop_order_iccprofile_info_t *profile)
static void _xyz_d50_to_profile_rgb(const dt_aligned_pixel_t XYZ_D50, const dt_iop_order_iccprofile_info_t *profile, dt_aligned_pixel_t RGB)
float dt_colorrings_graph_white(void)
void dt_colorrings_brightness_to_axis_rgb(const float brightness, const float white, const dt_iop_order_iccprofile_info_t *profile, dt_aligned_pixel_t RGB)
float dt_colorrings_dot3(const dt_aligned_pixel_t a, const dt_aligned_pixel_t b)
float dt_colorrings_curve_x_to_hue(const float x)
static void _dt_ucs_hsb_to_display_rgb_normalized(const dt_aligned_pixel_t HSB, const float white, const dt_iop_order_iccprofile_info_t *display_profile, dt_aligned_pixel_t RGB)
static void _xyz_d50_to_profile_linear_rgb(const dt_aligned_pixel_t XYZ_D50, const dt_iop_order_iccprofile_info_t *profile, dt_aligned_pixel_t RGB)
void dt_colorrings_fill_lut_local_field(float *lut, const int level, const float anchor_L[DT_COLORRINGS_LOCAL_FIELD_RINGS][DT_COLORRINGS_HUE_SAMPLES], const float anchor_rho[DT_COLORRINGS_LOCAL_FIELD_RINGS][DT_COLORRINGS_HUE_SAMPLES], const float anchor_theta[DT_COLORRINGS_LOCAL_FIELD_RINGS][DT_COLORRINGS_HUE_SAMPLES], const float delta_L[DT_COLORRINGS_LOCAL_FIELD_RINGS][DT_COLORRINGS_HUE_SAMPLES], const float chroma_scale[DT_COLORRINGS_LOCAL_FIELD_RINGS][DT_COLORRINGS_HUE_SAMPLES], const float delta_theta[DT_COLORRINGS_LOCAL_FIELD_RINGS][DT_COLORRINGS_HUE_SAMPLES], const float inv_sigma_L, const float inv_sigma_rho, const float inv_sigma_theta, const float rho0)
static void _dt_ucs_hsb_to_preview_rgb_unclamped(const dt_aligned_pixel_t HSB, const float white, dt_aligned_pixel_t RGB)
#define DT_COLORRINGS_HUE_SAMPLES
dt_colorrings_ring_t
@ DT_COLORRINGS_RING_DARK
@ DT_COLORRINGS_RING_MID
@ DT_COLORRINGS_RING_LIGHT
#define DT_COLORRINGS_MAXNODES
#define DT_COLORRINGS_NUM_RINGS
#define DT_COLORRINGS_ANGLE_SHIFT
#define DT_COLORRINGS_LOCAL_FIELD_RINGS
static float4 dt_UCS_JCH_to_HSB(const float4 JCH)
Definition colorspace.h:891
static float Y_to_dt_UCS_L_star(const float Y)
Definition colorspace.h:789
static float4 xyY_to_dt_UCS_JCH(const float4 xyY, const float L_white)
Definition colorspace.h:823
static float4 dt_XYZ_to_xyY(const float4 XYZ)
Definition colorspace.h:635
static dt_aligned_pixel_t xyY
static dt_aligned_pixel_t rgb
dt_apply_transposed_color_matrix(XYZ, xyz_to_srgb_matrix_transposed, sRGB)
dt_XYZ_to_sRGB(XYZ, result)
static dt_aligned_pixel_t XYZ_D65
static dt_aligned_pixel_t XYZ_D50
const dt_colormatrix_t dt_aligned_pixel_t out
static dt_aligned_pixel_t RGB
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.
#define MONOTONE_HERMITE
Definition curve_tools.h:35
#define for_each_channel(_var,...)
Definition darktable.h:662
#define __OMP_PARALLEL_FOR__(...)
Definition darktable.h:258
static const dt_aligned_pixel_simd_t value
Definition darktable.h:577
#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 M_PI_F
#define dt_pthread_rwlock_t
Definition dtpthread.h:389
#define dt_pthread_rwlock_unlock
Definition dtpthread.h:392
#define dt_pthread_rwlock_rdlock
Definition dtpthread.h:393
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)
static const float x
const float *const lut
const int t
const float v
float *const restrict const size_t k
dt_lut3d_interpolation_t
Definition lut3d.h:25
#define M_PI
Definition math.h:45
float dt_aligned_pixel_t[4]
float interpolate_val_V2_periodic(int n, CurveAnchorPoint Points[], float x, unsigned int type, float period)
Definition splines.cpp:730
const float r
dt_colormatrix_t matrix_out_transposed
Definition iop_profile.h:66
dt_colormatrix_t matrix_in_transposed
Definition iop_profile.h:65