38 _apply_trc(linear_rgb,
RGB, profile->
lut_out, profile->unbounded_coeffs_out, profile->
lutsize);
58 dt_UCS_HSB_to_XYZ(HSB, white,
XYZ_D65);
67 dt_aligned_pixel_t linear_rgb = { linear_rgb_in[0], linear_rgb_in[1], linear_rgb_in[2], 0.f };
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)
81 _apply_trc(linear_rgb,
RGB, display_profile->
lut_out, display_profile->unbounded_coeffs_out,
83 else if(display_profile)
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;
98 dt_UCS_HSB_to_XYZ(HSB, white,
XYZ_D65);
111 while(hue < 0.f) hue += 2.f *
M_PI_F;
134 const float distance = fabsf(x0 - x1);
135 return fminf(distance, 1.f - distance);
165 for(
int k = 0;
k < nodes;
k++)
167 anchors[
k].
x = curve[
k].
x;
168 anchors[
k].
y = curve[
k].
y;
177 const uint16_t clut_level, dt_pthread_rwlock_t *clut_lock,
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;
193 "colorrings swatch work to HLG Rec2020");
195 dt_lut3d_apply((
float *)output_rgb, (
float *)output_rgb, 1, clut, clut_level, 1.f, interpolation);
198 "colorrings swatch HLG Rec2020 to work");
200 output_rgb[0] *= normalized_white;
201 output_rgb[1] *= normalized_white;
202 output_rgb[2] *= normalized_white;
212 dt_UCS_HSB_to_XYZ(HSB, white,
XYZ_D65);
240 for_each_channel(c, aligned(display_rgb)) display_rgb[c] = CLAMP(display_rgb[c], 0.f, 1.f);
296 if(Ych[2] < 0.f) Ych[2] = 2.f *
M_PI_F + Ych[2];
309 for(
int iter = 0; iter < 18; iter++)
311 const float candidate = 0.5f * (low + high);
312 gboolean valid =
TRUE;
318 candidate, brightness, 0.f };
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)
341 if(reference_saturation[ring] == 0.f)
342 reference_saturation[ring]
352 return CLAMP((
RGB[0] +
RGB[1] +
RGB[2]) / 3.f, 0.f, 1.f);
367 float distance = INFINITY;
369 for(
int c = 0; c < 3; c++)
371 if(fabsf(direction[c]) < 1e-6f)
continue;
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;
378 return isfinite(distance) ? distance : 0.f;
388 if(shell_scale < 1.f)
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];
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);
402 return sqrtf(sqf(vector[0]) + sqf(vector[1]) + sqf(vector[2]));
407 return a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
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];
421 if(norm < 1e-6f)
return;
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);
442 const float eL0 = 0.5773502691896258f;
443 const float eL1 = 0.5773502691896258f;
444 const float eL2 = 0.5773502691896258f;
446 const float eu0 = 0.7071067811865475f;
447 const float eu1 = -0.7071067811865475f;
448 const float eu2 = 0.0f;
450 const float ev0 = 0.4082482904638630f;
451 const float ev1 = 0.4082482904638630f;
452 const float ev2 = -0.8164965809277260f;
454 *
L =
rgb[0] * eL0 +
rgb[1] * eL1 +
rgb[2] * eL2;
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;
459 *rho = sqrtf(u * u +
v *
v);
460 *theta = atan2f(
v, u);
465 const float eL0 = 0.5773502691896258f;
466 const float eL1 = 0.5773502691896258f;
467 const float eL2 = 0.5773502691896258f;
469 const float eu0 = 0.7071067811865475f;
470 const float eu1 = -0.7071067811865475f;
471 const float eu2 = 0.0f;
473 const float ev0 = 0.4082482904638630f;
474 const float ev1 = 0.4082482904638630f;
475 const float ev2 = -0.8164965809277260f;
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;
484 const float value =
L * 0.5773502691896258f;
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);
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;
513 const float inv_sigma_rho,
const float inv_sigma_theta,
const float rho0,
float out[3])
515 float Lx, rhox, thetax;
528 float sum_scale = 0.f;
529 float sum_dtheta = 0.f;
539 for(
int k = 0;
k <
n;
k++)
543 const float dL = (Lx - anchor_L[ring][h]) * inv_sigma_L;
544 const float dr = (rhox - anchor_rho[ring][h]) * inv_sigma_rho;
546 const float d2 = dL * dL + dr * dr + dh * dh;
548 if(d2 >= 1.f)
continue;
551 if(ring == axis_ring) w *= axis_weight_scale;
554 sum_dL += w * delta_L[ring][h];
555 sum_scale += w * chroma_scale[ring][h];
556 sum_dtheta += w * delta_theta[ring][h];
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)
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;
590 out[0] = target_rgb[0] -
x[0];
591 out[1] = target_rgb[1] -
x[1];
592 out[2] = target_rgb[2] -
x[2];
602 const float inv_sigma_rho,
const float inv_sigma_theta,
const float rho0)
605 for(
int b = 0; b < level; b++)
606 for(
int g = 0;
g < level;
g++)
607 for(
int r = 0;
r < level;
r++)
610 = { (float)
r / (
float)(level - 1), (
float)
g / (float)(level - 1), (float)b / (
float)(level - 1) };
613 inv_sigma_L, inv_sigma_rho, inv_sigma_theta, rho0,
d);
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);
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])
627 float Lx, rhox, thetax;
630 if(rhox <= 1e-6f ||
IS_NULL_PTR(anchors) || anchor_count <= 0)
640 float sum_scale = 0.f;
641 float sum_dtheta = 0.f;
649 for(
int k = 0;
k < anchor_count;
k++)
651 const float dL = (Lx - anchors[
k].
L) * inv_sigma_L;
652 const float dr = (rhox - anchors[
k].
rho) * inv_sigma_rho;
654 const float d2 = dL * dL + dr * dr + dh * dh;
656 if(d2 >= 1.f)
continue;
659 if(w <= FLT_MIN)
continue;
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)
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;
698 out[0] = target_rgb[0] -
x[0];
699 out[1] = target_rgb[1] -
x[1];
700 out[2] = target_rgb[2] -
x[2];
705 const int anchor_count,
const float inv_sigma_L,
706 const float inv_sigma_rho,
const float inv_sigma_theta,
710 for(
int b = 0; b < level; b++)
711 for(
int g = 0;
g < level;
g++)
712 for(
int r = 0;
r < level;
r++)
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)
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);
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_DARK
@ 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 float4 dt_UCS_JCH_to_HSB(const float4 JCH)
static float Y_to_dt_UCS_L_star(const float Y)
static float4 xyY_to_dt_UCS_JCH(const float4 xyY, const float L_white)
static float4 dt_XYZ_to_xyY(const float4 XYZ)
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
static int dt_pthread_rwlock_unlock(dt_pthread_rwlock_t *rwlock) RELEASE_GENERIC(rwlock) NO_THREAD_SAFETY_ANALYSIS
static int dt_pthread_rwlock_rdlock(dt_pthread_rwlock_t *rwlock) ACQUIRE_SHARED(rwlock) NO_THREAD_SAFETY_ANALYSIS
float *const restrict const size_t k
#define IS_NULL_PTR(p)
C is way too permissive with !=, == and if(var) checks, which can mean too many things depending on w...
#define __OMP_PARALLEL_FOR__(...)
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]
#define for_each_channel(_var,...)
static const dt_aligned_pixel_simd_t value
float interpolate_val_V2_periodic(int n, CurveAnchorPoint Points[], float x, unsigned int type, float period)
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_...
dt_colormatrix_t matrix_out_transposed
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]....
dt_colormatrix_t matrix_in_transposed