39 _apply_trc(linear_rgb,
RGB, profile->
lut_out, profile->unbounded_coeffs_out, profile->
lutsize);
59 dt_UCS_HSB_to_XYZ(HSB, white,
XYZ_D65);
68 dt_aligned_pixel_t linear_rgb = { linear_rgb_in[0], linear_rgb_in[1], linear_rgb_in[2], 0.f };
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)
82 _apply_trc(linear_rgb,
RGB, display_profile->
lut_out, display_profile->unbounded_coeffs_out,
84 else if(display_profile)
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;
99 dt_UCS_HSB_to_XYZ(HSB, white,
XYZ_D65);
112 while(hue < 0.f) hue += 2.f *
M_PI_F;
135 const float distance = fabsf(x0 - x1);
136 return fminf(distance, 1.f - distance);
166 for(
int k = 0;
k < nodes;
k++)
168 anchors[
k].
x = curve[
k].
x;
169 anchors[
k].
y = curve[
k].
y;
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;
194 "colorrings swatch work to HLG Rec2020");
196 dt_lut3d_apply((
float *)output_rgb, (
float *)output_rgb, 1, clut, clut_level, 1.f, interpolation);
199 "colorrings swatch HLG Rec2020 to work");
201 output_rgb[0] *= normalized_white;
202 output_rgb[1] *= normalized_white;
203 output_rgb[2] *= normalized_white;
213 dt_UCS_HSB_to_XYZ(HSB, white,
XYZ_D65);
241 for_each_channel(c, aligned(display_rgb)) display_rgb[c] = CLAMP(display_rgb[c], 0.f, 1.f);
297 if(Ych[2] < 0.f) Ych[2] = 2.f *
M_PI_F + Ych[2];
310 for(
int iter = 0; iter < 18; iter++)
312 const float candidate = 0.5f * (low + high);
313 gboolean valid =
TRUE;
319 candidate, brightness, 0.f };
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)
342 if(reference_saturation[ring] == 0.f)
343 reference_saturation[ring]
353 return CLAMP((
RGB[0] +
RGB[1] +
RGB[2]) / 3.f, 0.f, 1.f);
368 float distance = INFINITY;
370 for(
int c = 0; c < 3; c++)
372 if(fabsf(direction[c]) < 1e-6f)
continue;
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;
379 return isfinite(distance) ? distance : 0.f;
389 if(shell_scale < 1.f)
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];
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);
403 return sqrtf(sqf(vector[0]) + sqf(vector[1]) + sqf(vector[2]));
408 return a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
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];
422 if(norm < 1e-6f)
return;
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);
443 const float eL0 = 0.5773502691896258f;
444 const float eL1 = 0.5773502691896258f;
445 const float eL2 = 0.5773502691896258f;
447 const float eu0 = 0.7071067811865475f;
448 const float eu1 = -0.7071067811865475f;
449 const float eu2 = 0.0f;
451 const float ev0 = 0.4082482904638630f;
452 const float ev1 = 0.4082482904638630f;
453 const float ev2 = -0.8164965809277260f;
455 *
L =
rgb[0] * eL0 +
rgb[1] * eL1 +
rgb[2] * eL2;
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;
460 *rho = sqrtf(u * u +
v *
v);
461 *theta = atan2f(
v, u);
466 const float eL0 = 0.5773502691896258f;
467 const float eL1 = 0.5773502691896258f;
468 const float eL2 = 0.5773502691896258f;
470 const float eu0 = 0.7071067811865475f;
471 const float eu1 = -0.7071067811865475f;
472 const float eu2 = 0.0f;
474 const float ev0 = 0.4082482904638630f;
475 const float ev1 = 0.4082482904638630f;
476 const float ev2 = -0.8164965809277260f;
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;
485 const float value =
L * 0.5773502691896258f;
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);
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;
514 const float inv_sigma_rho,
const float inv_sigma_theta,
const float rho0,
float out[3])
516 float Lx, rhox, thetax;
529 float sum_scale = 0.f;
530 float sum_dtheta = 0.f;
540 for(
int k = 0;
k <
n;
k++)
544 const float dL = (Lx - anchor_L[ring][h]) * inv_sigma_L;
545 const float dr = (rhox - anchor_rho[ring][h]) * inv_sigma_rho;
547 const float d2 = dL * dL + dr * dr + dh * dh;
549 if(d2 >= 1.f)
continue;
552 if(ring == axis_ring) w *= axis_weight_scale;
555 sum_dL += w * delta_L[ring][h];
556 sum_scale += w * chroma_scale[ring][h];
557 sum_dtheta += w * delta_theta[ring][h];
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)
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;
591 out[0] = target_rgb[0] -
x[0];
592 out[1] = target_rgb[1] -
x[1];
593 out[2] = target_rgb[2] -
x[2];
603 const float inv_sigma_rho,
const float inv_sigma_theta,
const float rho0)
606 for(
int b = 0; b < level; b++)
607 for(
int g = 0;
g < level;
g++)
608 for(
int r = 0;
r < level;
r++)
611 = { (float)
r / (
float)(level - 1), (
float)
g / (float)(level - 1), (float)b / (
float)(level - 1) };
614 inv_sigma_L, inv_sigma_rho, inv_sigma_theta, rho0,
d);
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);
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])
628 float Lx, rhox, thetax;
631 if(rhox <= 1e-6f ||
IS_NULL_PTR(anchors) || anchor_count <= 0)
641 float sum_scale = 0.f;
642 float sum_dtheta = 0.f;
650 for(
int k = 0;
k < anchor_count;
k++)
652 const float dL = (Lx - anchors[
k].
L) * inv_sigma_L;
653 const float dr = (rhox - anchors[
k].
rho) * inv_sigma_rho;
655 const float d2 = dL * dL + dr * dr + dh * dh;
657 if(d2 >= 1.f)
continue;
660 if(w <= FLT_MIN)
continue;
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)
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;
699 out[0] = target_rgb[0] -
x[0];
700 out[1] = target_rgb[1] -
x[1];
701 out[2] = target_rgb[2] -
x[2];
706 const int anchor_count,
const float inv_sigma_L,
707 const float inv_sigma_rho,
const float inv_sigma_theta,
711 for(
int b = 0; b < level; b++)
712 for(
int g = 0;
g < level;
g++)
713 for(
int r = 0;
r < level;
r++)
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)
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);
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
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
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 for_each_channel(_var,...)
#define __OMP_PARALLEL_FOR__(...)
static const dt_aligned_pixel_simd_t value
#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 dt_pthread_rwlock_t
#define dt_pthread_rwlock_unlock
#define dt_pthread_rwlock_rdlock
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)
float *const restrict const size_t k
float dt_aligned_pixel_t[4]
float interpolate_val_V2_periodic(int n, CurveAnchorPoint Points[], float x, unsigned int type, float period)
dt_colormatrix_t matrix_out_transposed
dt_colormatrix_t matrix_in_transposed