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"
24#include "math/splines.h"
25
26#include <float.h>
27#include <math.h>
28#include <string.h>
29
31 const dt_iop_order_iccprofile_info_t *profile,
33{
34 dt_aligned_pixel_t linear_rgb = { 0.f };
36
37 if(profile->nonlinearlut)
38 _apply_trc(linear_rgb, RGB, profile->lut_out, profile->unbounded_coeffs_out, profile->lutsize);
39 else
40 for_each_channel(c, aligned(RGB, linear_rgb)) RGB[c] = linear_rgb[c];
41}
42
44 const dt_iop_order_iccprofile_info_t *profile,
46{
47 if(profile)
49 else
50 dt_XYZ_to_linearRGB(XYZ_D50, RGB);
51}
52
53static inline void _dt_ucs_hsb_to_preview_rgb_unclamped(const dt_aligned_pixel_t HSB, const float white,
55{
58 dt_UCS_HSB_to_XYZ(HSB, white, XYZ_D65);
61}
62
64 const dt_iop_order_iccprofile_info_t *display_profile,
66{
67 dt_aligned_pixel_t linear_rgb = { linear_rgb_in[0], linear_rgb_in[1], linear_rgb_in[2], 0.f };
68
76 const float max_rgb = fmaxf(linear_rgb[0], fmaxf(linear_rgb[1], linear_rgb[2]));
77 if(linear_rgb[0] > 1.f || linear_rgb[1] > 1.f || linear_rgb[2] > 1.f)
78 for_each_channel(c, aligned(linear_rgb)) linear_rgb[c] /= max_rgb;
79
80 if(display_profile && display_profile->nonlinearlut)
81 _apply_trc(linear_rgb, RGB, display_profile->lut_out, display_profile->unbounded_coeffs_out,
82 display_profile->lutsize);
83 else if(display_profile)
84 for_each_channel(c, aligned(RGB, linear_rgb)) RGB[c] = linear_rgb[c];
85 else
86 for_each_channel(c, aligned(RGB, linear_rgb))
87 RGB[c] = linear_rgb[c] <= 0.0031308f ? 12.92f * linear_rgb[c]
88 : (1.0f + 0.055f) * powf(linear_rgb[c], 1.0f / 2.4f) - 0.055f;
89}
90
91static inline void _dt_ucs_hsb_to_display_rgb_normalized(const dt_aligned_pixel_t HSB, const float white,
92 const dt_iop_order_iccprofile_info_t *display_profile,
94{
97 dt_aligned_pixel_t linear_rgb = { 0.f };
98 dt_UCS_HSB_to_XYZ(HSB, white, XYZ_D65);
100 _xyz_d50_to_profile_linear_rgb(XYZ_D50, display_profile, linear_rgb);
101 _profile_linear_rgb_to_display_rgb_normalized(linear_rgb, display_profile, RGB);
102}
103
105{
106 return Y_to_dt_UCS_L_star(1.f);
107}
108
110{
111 while(hue < 0.f) hue += 2.f * M_PI_F;
112 while(hue >= 2.f * M_PI_F) hue -= 2.f * M_PI_F;
113 return hue;
114}
115
117{
119 return hue - M_PI_F;
120}
121
123{
124 return dt_colorrings_wrap_hue_pi((360.f * x + DT_COLORRINGS_ANGLE_SHIFT) * M_PI_F / 180.f);
125}
126
127float dt_colorrings_hue_to_curve_x(const float hue)
128{
129 return dt_colorrings_wrap_hue_2pi(hue - DT_COLORRINGS_ANGLE_SHIFT * M_PI_F / 180.f) / (2.f * M_PI_F);
130}
131
132float dt_colorrings_curve_periodic_distance(const float x0, const float x1)
133{
134 const float distance = fabsf(x0 - x1);
135 return fminf(distance, 1.f - distance);
136}
137
139{
140 switch(ring)
141 {
143 return 0.15f;
145 return 0.75f;
147 default:
148 return 0.45f;
149 }
150}
151
152float dt_colorrings_curve_periodic_sample(const dt_colorrings_node_t *curve, const int nodes, const float x)
153{
161 if(IS_NULL_PTR(curve) || nodes < 2) return 0.5f;
162
164
165 for(int k = 0; k < nodes; k++)
166 {
167 anchors[k].x = curve[k].x;
168 anchors[k].y = curve[k].y;
169 }
170
171 return interpolate_val_V2_periodic(nodes, anchors, x, MONOTONE_HERMITE, 1.f);
172}
173
174gboolean dt_colorrings_apply_rgb_lut(const dt_aligned_pixel_t input_rgb, const float white_level,
175 const dt_iop_order_iccprofile_info_t *work_profile,
176 const dt_iop_order_iccprofile_info_t *lut_profile, const float *clut,
177 const uint16_t clut_level, dt_pthread_rwlock_t *clut_lock,
178 const dt_lut3d_interpolation_t interpolation, dt_aligned_pixel_t output_rgb)
179{
180 if(IS_NULL_PTR(output_rgb)) return FALSE;
181
182 memcpy(output_rgb, input_rgb, sizeof(dt_aligned_pixel_t));
183
184 if(IS_NULL_PTR(work_profile) || IS_NULL_PTR(lut_profile) || IS_NULL_PTR(clut) || clut_level == 0) return FALSE;
185
186 const float normalized_white = fmaxf(white_level, 1e-6f);
187 output_rgb[0] = input_rgb[0] / normalized_white;
188 output_rgb[1] = input_rgb[1] / normalized_white;
189 output_rgb[2] = input_rgb[2] / normalized_white;
190 output_rgb[3] = 0.f;
191
192 dt_ioppr_transform_image_colorspace_rgb((float *)output_rgb, (float *)output_rgb, 1, 1, work_profile, lut_profile,
193 "colorrings swatch work to HLG Rec2020");
194 if(clut_lock) dt_pthread_rwlock_rdlock(clut_lock);
195 dt_lut3d_apply((float *)output_rgb, (float *)output_rgb, 1, clut, clut_level, 1.f, interpolation);
196 if(clut_lock) dt_pthread_rwlock_unlock(clut_lock);
197 dt_ioppr_transform_image_colorspace_rgb((float *)output_rgb, (float *)output_rgb, 1, 1, lut_profile, work_profile,
198 "colorrings swatch HLG Rec2020 to work");
199
200 output_rgb[0] *= normalized_white;
201 output_rgb[1] *= normalized_white;
202 output_rgb[2] *= normalized_white;
203 output_rgb[3] = 0.f;
204 return TRUE;
205}
206
207void dt_colorrings_hsb_to_profile_rgb(const dt_aligned_pixel_t HSB, const float white,
209{
210 dt_aligned_pixel_t XYZ_D65 = { 0.f };
211 dt_aligned_pixel_t XYZ_D50 = { 0.f };
212 dt_UCS_HSB_to_XYZ(HSB, white, XYZ_D65);
215}
216
217void dt_colorrings_hsb_to_display_rgb(const dt_aligned_pixel_t HSB, const float white,
219{
220 _dt_ucs_hsb_to_display_rgb_normalized(HSB, white, display_profile, RGB);
221 for_each_channel(c, aligned(RGB)) RGB[c] = CLAMP(RGB[c], 0.f, 1.f);
222}
223
225 const dt_iop_order_iccprofile_info_t *profile,
226 const dt_iop_order_iccprofile_info_t *display_profile,
227 dt_aligned_pixel_t display_rgb)
228{
229 dt_aligned_pixel_t XYZ_D50 = { 0.f };
230 dt_aligned_pixel_t linear_rgb = { 0.f };
231
232 if(profile)
233 dt_ioppr_rgb_matrix_to_xyz(RGB, XYZ_D50, profile->matrix_in_transposed, profile->lut_in,
234 profile->unbounded_coeffs_in, profile->lutsize, profile->nonlinearlut);
235 else
236 dt_linearRGB_to_XYZ(RGB, XYZ_D50);
237
238 _xyz_d50_to_profile_linear_rgb(XYZ_D50, display_profile, linear_rgb);
239 _profile_linear_rgb_to_display_rgb_normalized(linear_rgb, display_profile, display_rgb);
240 for_each_channel(c, aligned(display_rgb)) display_rgb[c] = CLAMP(display_rgb[c], 0.f, 1.f);
241}
242
245{
246 dt_aligned_pixel_t XYZ_D50 = { 0.f };
247 dt_aligned_pixel_t XYZ_D65 = { 0.f };
248 dt_aligned_pixel_t xyY = { 0.f };
249
250 if(IS_NULL_PTR(profile))
251 {
252 memset(JCH, 0, sizeof(dt_aligned_pixel_t));
253 return;
254 }
255
256 dt_ioppr_rgb_matrix_to_xyz(RGB, XYZ_D50, profile->matrix_in_transposed, profile->lut_in,
257 profile->unbounded_coeffs_in, profile->lutsize, profile->nonlinearlut);
259 for_each_channel(c, aligned(XYZ_D65)) XYZ_D65[c] = fmaxf(XYZ_D65[c], 0.f);
260
261 if(XYZ_D65[0] + XYZ_D65[1] + XYZ_D65[2] <= 1e-6f)
262 {
263 memset(JCH, 0, sizeof(dt_aligned_pixel_t));
264 return;
265 }
266
268 xyY_to_dt_UCS_JCH(xyY, white, JCH);
269}
270
273{
274 dt_aligned_pixel_t JCH = { 0.f };
275 dt_colorrings_profile_rgb_to_dt_ucs_jch(RGB, white, profile, JCH);
276 dt_UCS_JCH_to_HSB(JCH, HSB);
277}
278
281{
282 dt_aligned_pixel_t XYZ_D50 = { 0.f };
283 dt_aligned_pixel_t XYZ_D65 = { 0.f };
284
285 if(IS_NULL_PTR(profile))
286 {
287 memset(Ych, 0, sizeof(dt_aligned_pixel_t));
288 return;
289 }
290
291 dt_ioppr_rgb_matrix_to_xyz(RGB, XYZ_D50, profile->matrix_in_transposed, profile->lut_in,
292 profile->unbounded_coeffs_in, profile->lutsize, profile->nonlinearlut);
294 XYZ_to_Ych(XYZ_D65, Ych);
295
296 if(Ych[2] < 0.f) Ych[2] = 2.f * M_PI_F + Ych[2];
297}
298
299static float _compute_reference_saturation(const float white, const float brightness)
300{
301 float low = 0.f;
302 float high = 1.f;
303
309 for(int iter = 0; iter < 18; iter++)
310 {
311 const float candidate = 0.5f * (low + high);
312 gboolean valid = TRUE;
313
314 for(int hue = 0; hue < DT_COLORRINGS_HUE_SAMPLES; hue++)
315 {
316 dt_aligned_pixel_t RGB = { 0.f };
318 candidate, brightness, 0.f };
320
321 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)
322 {
323 valid = FALSE;
324 break;
325 }
326 }
327
328 if(valid)
329 low = candidate;
330 else
331 high = candidate;
332 }
333
334 return low;
335}
336
338 float reference_saturation[DT_COLORRINGS_NUM_RINGS])
339{
340 for(int ring = 0; ring < DT_COLORRINGS_NUM_RINGS; ring++)
341 if(reference_saturation[ring] == 0.f)
342 reference_saturation[ring]
344}
345
346float dt_colorrings_ring_axis_position_from_brightness(const float brightness, const float white,
347 const dt_iop_order_iccprofile_info_t *profile)
348{
349 const dt_aligned_pixel_t HSB = { 0.f, 0.f, CLAMP(brightness, 0.f, 1.f), 0.f };
350 dt_aligned_pixel_t RGB = { 0.f };
351 dt_colorrings_hsb_to_profile_rgb(HSB, white, profile, RGB);
352 return CLAMP((RGB[0] + RGB[1] + RGB[2]) / 3.f, 0.f, 1.f);
353}
354
355void dt_colorrings_brightness_to_axis_rgb(const float brightness, const float white,
357{
358 const float axis = dt_colorrings_ring_axis_position_from_brightness(brightness, white, profile);
359 RGB[0] = axis;
360 RGB[1] = axis;
361 RGB[2] = axis;
362 RGB[3] = 0.f;
363}
364
366{
367 float distance = INFINITY;
368
369 for(int c = 0; c < 3; c++)
370 {
371 if(fabsf(direction[c]) < 1e-6f) continue;
372
373 const float bound = (direction[c] > 0.f) ? 1.f : 0.f;
374 const float candidate = (bound - axis[c]) / direction[c];
375 if(candidate > 0.f && candidate < distance) distance = candidate;
376 }
377
378 return isfinite(distance) ? distance : 0.f;
379}
380
382{
383 dt_aligned_pixel_t vector = { RGB[0] - axis[0], RGB[1] - axis[1], RGB[2] - axis[2], 0.f };
384
385 if(dt_colorrings_vector_norm3(vector) < 1e-6f) return;
386
387 const float shell_scale = dt_colorrings_distance_to_cube_shell(axis, vector);
388 if(shell_scale < 1.f)
389 {
390 RGB[0] = axis[0] + shell_scale * vector[0];
391 RGB[1] = axis[1] + shell_scale * vector[1];
392 RGB[2] = axis[2] + shell_scale * vector[2];
393 }
394
395 RGB[0] = CLAMP(RGB[0], 0.f, 1.f);
396 RGB[1] = CLAMP(RGB[1], 0.f, 1.f);
397 RGB[2] = CLAMP(RGB[2], 0.f, 1.f);
398}
399
401{
402 return sqrtf(sqf(vector[0]) + sqf(vector[1]) + sqf(vector[2]));
403}
404
406{
407 return a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
408}
409
411{
412 out[0] = a[1] * b[2] - a[2] * b[1];
413 out[1] = a[2] * b[0] - a[0] * b[2];
414 out[2] = a[0] * b[1] - a[1] * b[0];
415 out[3] = 0.f;
416}
417
419{
420 const float norm = dt_colorrings_vector_norm3(vector);
421 if(norm < 1e-6f) return;
422
423 vector[0] /= norm;
424 vector[1] /= norm;
425 vector[2] /= norm;
426}
427
429 const float cos_angle, const float sin_angle, dt_aligned_pixel_t output)
430{
431 dt_aligned_pixel_t cross = { 0.f };
432 dt_colorrings_cross3(axis, input, cross);
433 const float axis_dot = dt_colorrings_dot3(axis, input);
434
435 for(int c = 0; c < 3; c++)
436 output[c] = input[c] * cos_angle + cross[c] * sin_angle + axis[c] * axis_dot * (1.f - cos_angle);
437 output[3] = 0.f;
438}
439
440void dt_colorrings_rgb_to_gray_cyl(const float rgb[3], float *L, float *rho, float *theta)
441{
442 const float eL0 = 0.5773502691896258f;
443 const float eL1 = 0.5773502691896258f;
444 const float eL2 = 0.5773502691896258f;
445
446 const float eu0 = 0.7071067811865475f;
447 const float eu1 = -0.7071067811865475f;
448 const float eu2 = 0.0f;
449
450 const float ev0 = 0.4082482904638630f;
451 const float ev1 = 0.4082482904638630f;
452 const float ev2 = -0.8164965809277260f;
453
454 *L = rgb[0] * eL0 + rgb[1] * eL1 + rgb[2] * eL2;
455
456 const float u = rgb[0] * eu0 + rgb[1] * eu1 + rgb[2] * eu2;
457 const float v = rgb[0] * ev0 + rgb[1] * ev1 + rgb[2] * ev2;
458
459 *rho = sqrtf(u * u + v * v);
460 *theta = atan2f(v, u);
461}
462
463void dt_colorrings_gray_basis_to_rgb(const float L, const float u, const float v, float rgb[3])
464{
465 const float eL0 = 0.5773502691896258f;
466 const float eL1 = 0.5773502691896258f;
467 const float eL2 = 0.5773502691896258f;
468
469 const float eu0 = 0.7071067811865475f;
470 const float eu1 = -0.7071067811865475f;
471 const float eu2 = 0.0f;
472
473 const float ev0 = 0.4082482904638630f;
474 const float ev1 = 0.4082482904638630f;
475 const float ev2 = -0.8164965809277260f;
476
477 rgb[0] = L * eL0 + u * eu0 + v * ev0;
478 rgb[1] = L * eL1 + u * eu1 + v * ev1;
479 rgb[2] = L * eL2 + u * eu2 + v * ev2;
480}
481
483{
484 const float value = L * 0.5773502691896258f;
485 RGB[0] = value;
486 RGB[1] = value;
487 RGB[2] = value;
488 RGB[3] = 0.f;
489}
490
492{
493 if(d >= 1.0f) return 0.0f;
494 const float t = 1.0f - d;
495 return t * t * t * t * (4.0f * d + 1.0f);
496}
497
499{
500 const float two_pi = 2.0f * (float)M_PI;
501 while(x <= -(float)M_PI) x += two_pi;
502 while(x > (float)M_PI) x -= two_pi;
503 return x;
504}
505
507 const float x[3], const float anchor_L[DT_COLORRINGS_LOCAL_FIELD_RINGS][DT_COLORRINGS_HUE_SAMPLES],
512 const float delta_theta[DT_COLORRINGS_LOCAL_FIELD_RINGS][DT_COLORRINGS_HUE_SAMPLES], const float inv_sigma_L,
513 const float inv_sigma_rho, const float inv_sigma_theta, const float rho0, float out[3])
514{
515 float Lx, rhox, thetax;
516 dt_colorrings_rgb_to_gray_cyl(x, &Lx, &rhox, &thetax);
517
518 if(rhox <= 1e-6f)
519 {
520 out[0] = 0.f;
521 out[1] = 0.f;
522 out[2] = 0.f;
523 return;
524 }
525
526 float sum_w = 0.f;
527 float sum_dL = 0.f;
528 float sum_scale = 0.f;
529 float sum_dtheta = 0.f;
530
532 const int axis_ring = DT_COLORRINGS_LOCAL_FIELD_RINGS - 1;
533 const float axis_weight_scale = 1.0f / (float)DT_COLORRINGS_HUE_SAMPLES;
534
539 for(int k = 0; k < n; k++)
540 {
541 const int ring = k / DT_COLORRINGS_HUE_SAMPLES;
542 const int h = k - ring * DT_COLORRINGS_HUE_SAMPLES;
543 const float dL = (Lx - anchor_L[ring][h]) * inv_sigma_L;
544 const float dr = (rhox - anchor_rho[ring][h]) * inv_sigma_rho;
545 const float dh = dt_colorrings_wrap_pi(thetax - anchor_theta[ring][h]) * inv_sigma_theta;
546 const float d2 = dL * dL + dr * dr + dh * dh;
547
548 if(d2 >= 1.f) continue;
549
550 float w = dt_colorrings_wendland_c2(sqrtf(d2));
551 if(ring == axis_ring) w *= axis_weight_scale;
552
553 sum_w += w;
554 sum_dL += w * delta_L[ring][h];
555 sum_scale += w * chroma_scale[ring][h];
556 sum_dtheta += w * delta_theta[ring][h];
557 }
558
559 if(sum_w <= FLT_MIN)
560 {
561 out[0] = 0.f;
562 out[1] = 0.f;
563 out[2] = 0.f;
564 return;
565 }
566
567 const float inv_w = 1.0f / sum_w;
568 const float target_delta_L = sum_dL * inv_w;
569 const float scale = sum_scale * inv_w;
570 const float target_delta_theta = sum_dtheta * inv_w;
571 if(fabsf(target_delta_L) <= 1e-6f && fabsf(scale - 1.f) <= 1e-6f && fabsf(target_delta_theta) <= 1e-6f)
572 {
573 out[0] = 0.f;
574 out[1] = 0.f;
575 out[2] = 0.f;
576 return;
577 }
578 const float t = CLAMP(rhox / rho0, 0.f, 1.f);
579 const float alpha = t * t * (3.0f - 2.0f * t);
580 const float target_L = Lx + alpha * target_delta_L;
581 const float target_rho = rhox * fmaxf(1.f + alpha * (scale - 1.f), 0.f);
582 const float target_theta = thetax + alpha * target_delta_theta;
583 dt_aligned_pixel_t target_rgb = { 0.f };
584 dt_aligned_pixel_t axis = { 0.f };
585 dt_colorrings_gray_basis_to_rgb(target_L, target_rho * cosf(target_theta), target_rho * sinf(target_theta),
586 target_rgb);
588 dt_colorrings_project_to_cube_shell(axis, target_rgb);
589
590 out[0] = target_rgb[0] - x[0];
591 out[1] = target_rgb[1] - x[1];
592 out[2] = target_rgb[2] - x[2];
593}
594
596 float *lut, const int level, const float anchor_L[DT_COLORRINGS_LOCAL_FIELD_RINGS][DT_COLORRINGS_HUE_SAMPLES],
601 const float delta_theta[DT_COLORRINGS_LOCAL_FIELD_RINGS][DT_COLORRINGS_HUE_SAMPLES], const float inv_sigma_L,
602 const float inv_sigma_rho, const float inv_sigma_theta, const float rho0)
603{
604 __OMP_PARALLEL_FOR__(collapse(3))
605 for(int b = 0; b < level; b++)
606 for(int g = 0; g < level; g++)
607 for(int r = 0; r < level; r++)
608 {
609 const float x[3]
610 = { (float)r / (float)(level - 1), (float)g / (float)(level - 1), (float)b / (float)(level - 1) };
611 float d[3];
612 dt_colorrings_eval_local_field(x, anchor_L, anchor_rho, anchor_theta, delta_L, chroma_scale, delta_theta,
613 inv_sigma_L, inv_sigma_rho, inv_sigma_theta, rho0, d);
614
615 const size_t idx = (((size_t)b * level + (size_t)g) * level + (size_t)r) * 3u;
616 lut[idx + 0] = CLAMP(x[0] + d[0], 0.f, 1.f);
617 lut[idx + 1] = CLAMP(x[1] + d[1], 0.f, 1.f);
618 lut[idx + 2] = CLAMP(x[2] + d[2], 0.f, 1.f);
619 }
620}
621
623 const int anchor_count, const float inv_sigma_L,
624 const float inv_sigma_rho, const float inv_sigma_theta,
625 const float rho0, float out[3])
626{
627 float Lx, rhox, thetax;
628 dt_colorrings_rgb_to_gray_cyl(x, &Lx, &rhox, &thetax);
629
630 if(rhox <= 1e-6f || IS_NULL_PTR(anchors) || anchor_count <= 0)
631 {
632 out[0] = 0.f;
633 out[1] = 0.f;
634 out[2] = 0.f;
635 return;
636 }
637
638 float sum_w = 0.f;
639 float sum_dL = 0.f;
640 float sum_scale = 0.f;
641 float sum_dtheta = 0.f;
642
649 for(int k = 0; k < anchor_count; k++)
650 {
651 const float dL = (Lx - anchors[k].L) * inv_sigma_L;
652 const float dr = (rhox - anchors[k].rho) * inv_sigma_rho;
653 const float dh = dt_colorrings_wrap_pi(thetax - anchors[k].theta) * inv_sigma_theta;
654 const float d2 = dL * dL + dr * dr + dh * dh;
655
656 if(d2 >= 1.f) continue;
657
658 const float w = anchors[k].weight * dt_colorrings_wendland_c2(sqrtf(d2));
659 if(w <= FLT_MIN) continue;
660
661 sum_w += w;
662 sum_dL += w * anchors[k].delta_L;
663 sum_scale += w * anchors[k].chroma_scale;
664 sum_dtheta += w * anchors[k].delta_theta;
665 }
666
667 if(sum_w <= FLT_MIN)
668 {
669 out[0] = 0.f;
670 out[1] = 0.f;
671 out[2] = 0.f;
672 return;
673 }
674
675 const float inv_w = 1.0f / sum_w;
676 const float target_delta_L = sum_dL * inv_w;
677 const float scale = sum_scale * inv_w;
678 const float target_delta_theta = sum_dtheta * inv_w;
679 if(fabsf(target_delta_L) <= 1e-6f && fabsf(scale - 1.f) <= 1e-6f && fabsf(target_delta_theta) <= 1e-6f)
680 {
681 out[0] = 0.f;
682 out[1] = 0.f;
683 out[2] = 0.f;
684 return;
685 }
686 const float t = CLAMP(rhox / rho0, 0.f, 1.f);
687 const float alpha = t * t * (3.0f - 2.0f * t);
688 const float target_L = Lx + alpha * target_delta_L;
689 const float target_rho = rhox * fmaxf(1.f + alpha * (scale - 1.f), 0.f);
690 const float target_theta = thetax + alpha * target_delta_theta;
691 dt_aligned_pixel_t target_rgb = { 0.f };
692 dt_aligned_pixel_t axis = { 0.f };
693 dt_colorrings_gray_basis_to_rgb(target_L, target_rho * cosf(target_theta), target_rho * sinf(target_theta),
694 target_rgb);
696 dt_colorrings_project_to_cube_shell(axis, target_rgb);
697
698 out[0] = target_rgb[0] - x[0];
699 out[1] = target_rgb[1] - x[1];
700 out[2] = target_rgb[2] - x[2];
701}
702
704 const dt_colorrings_sparse_anchor_t *const anchors,
705 const int anchor_count, const float inv_sigma_L,
706 const float inv_sigma_rho, const float inv_sigma_theta,
707 const float rho0)
708{
709 __OMP_PARALLEL_FOR__(collapse(3))
710 for(int b = 0; b < level; b++)
711 for(int g = 0; g < level; g++)
712 for(int r = 0; r < level; r++)
713 {
714 const float x[3]
715 = { (float)r / (float)(level - 1), (float)g / (float)(level - 1), (float)b / (float)(level - 1) };
716 float d[3] = { 0.f };
717 if(anchors && anchor_count > 0)
718 dt_colorrings_eval_sparse_local_field(x, anchors, anchor_count, inv_sigma_L, inv_sigma_rho, inv_sigma_theta,
719 rho0, d);
720
721 const size_t idx = (((size_t)b * level + (size_t)g) * level + (size_t)r) * 3u;
722 lut[idx + 0] = CLAMP(x[0] + d[0], 0.f, 1.f);
723 lut[idx + 1] = CLAMP(x[1] + d[1], 0.f, 1.f);
724 lut[idx + 2] = CLAMP(x[2] + d[2], 0.f, 1.f);
725 }
726}
#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
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
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
#define MONOTONE_HERMITE
Definition curve_tools.h:36
#define M_PI_F
static int dt_pthread_rwlock_unlock(dt_pthread_rwlock_t *rwlock) RELEASE_GENERIC(rwlock) NO_THREAD_SAFETY_ANALYSIS
Definition dtpthread.h:217
static int dt_pthread_rwlock_rdlock(dt_pthread_rwlock_t *rwlock) ACQUIRE_SHARED(rwlock) NO_THREAD_SAFETY_ANALYSIS
Definition dtpthread.h:267
float *const restrict const size_t k
dt_lut3d_interpolation_t
Definition lut3d.h:26
#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 macros.h:96
#define M_PI
Definition math.h:47
#define __OMP_PARALLEL_FOR__(...)
Definition openmp.h:95
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_ALIGNED_PIXEL float dt_aligned_pixel_t[4]
Definition simd.h:53
#define for_each_channel(_var,...)
Definition simd.h:87
static const dt_aligned_pixel_simd_t value
Definition simd.h:144
float interpolate_val_V2_periodic(int n, CurveAnchorPoint Points[], float x, unsigned int type, float period)
Definition splines.cpp:729
const float r
A profile reduced to the arithmetic the pixel loop can run: two matrices and six tone-curve LUTs,...
int nonlinearlut
Non-zero when the profile has tone curves at all; tested as a boolean everywhere, but it is really th...
int lutsize
Entry count of each of the six LUTs. Always 65536 in practice: both callers of dt_ioppr_init_profile_...
float * lut_out[3]
Per-channel linear -> encoded tone curve, same convention as lut_in.
float * lut_in[3]
Per-channel encoded -> linear tone curve, lutsize entries each, sampled over [0,1]....