19#include <glib/gstdio.h>
42#define DT_LUT_VIEWER_MARGIN DT_PIXEL_APPLY_DPI(12)
43#define DT_LUT_VIEWER_TARGET_SAMPLES 4096
44#define DT_LUT_VIEWER_AXIS_LENGTH DT_PIXEL_APPLY_DPI(20.f)
156static inline dt_aligned_pixel_simd_t
_set_vector(
const float x,
const float y,
const float z)
158 return (dt_aligned_pixel_simd_t){
x, y, z, 0.f };
161static inline float _dot3(
const dt_aligned_pixel_simd_t a,
const dt_aligned_pixel_simd_t b)
163 return a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
166static inline dt_aligned_pixel_simd_t
_cross3(
const dt_aligned_pixel_simd_t a,
const dt_aligned_pixel_simd_t b)
169 a[2] * b[0] - a[0] * b[2],
170 a[0] * b[1] - a[1] * b[0]);
173static inline dt_aligned_pixel_simd_t
_normalize3(
const dt_aligned_pixel_simd_t vector)
175 const float norm = sqrtf(
_dot3(vector, vector));
176 if(norm < 1e-6f)
return vector;
178 const float inv_norm = 1.f / norm;
179 return (dt_aligned_pixel_simd_t){ vector[0] * inv_norm, vector[1] * inv_norm, vector[2] * inv_norm, 0.f };
184 while(angle <= -180.f) angle += 360.f;
185 while(angle > 180.f) angle -= 360.f;
190 const dt_aligned_pixel_simd_t output_rgb)
192 const float dr = output_rgb[0] - input_rgb[0];
193 const float dg = output_rgb[1] - input_rgb[1];
194 const float db = output_rgb[2] - input_rgb[2];
195 return 100.f * sqrtf((dr * dr + dg * dg + db * db) / 3.f);
200 dt_aligned_pixel_simd_t
out = dt_simd_max_zero(
value);
202 for(
int c = 0; c < 3; c++)
203 out[c] = fminf(
out[c], 1.f);
215 dt_aligned_pixel_simd_t *display_rgb,
const size_t count)
217 if(
IS_NULL_PTR(source_rgb) || !display_rgb || count == 0)
return;
219 for(
size_t k = 0;
k < count;
k++)
230 const dt_aligned_pixel_simd_t input,
238 profile_info_from->
lut_in, profile_info_from->unbounded_coeffs_in,
245 _apply_trc(linear_rgb, output, profile_info_to->
lut_out, profile_info_to->unbounded_coeffs_out,
255 const dt_aligned_pixel_simd_t work_rgb)
257 dt_aligned_pixel_simd_t display_rgb =
_clamp01_simd(work_rgb);
265 dt_aligned_pixel_simd_t in = work_rgb;
266 dt_aligned_pixel_simd_t
out = work_rgb;
268 "lut viewer swatch");
274 dt_aligned_pixel_simd_t *display_rgb,
const size_t count,
const char *message)
276 if(
IS_NULL_PTR(work_rgb) || !display_rgb || count == 0)
return;
290 cairo_surface_destroy(viewer->
surface);
397 const cmsCIEXYZ *red = cmsReadTag(profile->
profile, cmsSigRedColorantTag);
398 const cmsCIEXYZ *green = cmsReadTag(profile->
profile, cmsSigGreenColorantTag);
399 const cmsCIEXYZ *blue = cmsReadTag(profile->
profile, cmsSigBlueColorantTag);
403 rgb_to_xyz[0][0] = red->X;
404 rgb_to_xyz[0][1] = green->X;
405 rgb_to_xyz[0][2] = blue->X;
406 rgb_to_xyz[1][0] = red->Y;
407 rgb_to_xyz[1][1] = green->Y;
408 rgb_to_xyz[1][2] = blue->Y;
409 rgb_to_xyz[2][0] = red->Z;
410 rgb_to_xyz[2][1] = green->Z;
411 rgb_to_xyz[2][2] = blue->Z;
418 const dt_aligned_pixel_simd_t
rgb)
420 const float epsilon = 1e-3f;
421 dt_aligned_pixel_simd_t xyz = { 0.f };
422 dt_aligned_pixel_simd_t gamut_rgb = { 0.f };
424 dot_product((
float *)&xyz, xyz_to_rgb, (
float *)&gamut_rgb);
432 return gamut_rgb[0] >= -epsilon && gamut_rgb[0] <= 1.f + epsilon
433 && gamut_rgb[1] >= -epsilon && gamut_rgb[1] <= 1.f + epsilon
434 && gamut_rgb[2] >= -epsilon && gamut_rgb[2] <= 1.f + epsilon;
438 const float rotation_of_axis,
const float slice_depth,
const float slice_thickness,
440 const float pan_x,
const float pan_y,
const int width,
const int height)
442 static const dt_aligned_pixel_simd_t axis = { 0.5773502691896258f, 0.5773502691896258f, 0.5773502691896258f, 0.f };
443 static const dt_aligned_pixel_simd_t chroma_x = { 0.7071067811865475f, -0.7071067811865475f, 0.f, 0.f };
444 static const dt_aligned_pixel_simd_t chroma_y = { 0.4082482904638630f, 0.4082482904638630f, -0.8164965809277260f, 0.f };
446 const float azimuth = rotation_around_axis *
M_PI_F / 180.f;
447 const float tilt = rotation_of_axis *
M_PI_F / 180.f;
448 dt_aligned_pixel_simd_t rotated_x = { 0.f };
449 dt_aligned_pixel_simd_t rotated_y = { 0.f };
451 for(
int c = 0; c < 3; c++)
453 rotated_x[c] = cosf(azimuth) * chroma_x[c] + sinf(azimuth) * chroma_y[c];
454 rotated_y[c] = -sinf(azimuth) * chroma_x[c] + cosf(azimuth) * chroma_y[c];
455 projection->
screen_z[c] = cosf(tilt) * rotated_y[c] + sinf(tilt) * axis[c];
456 projection->
screen_x[c] = rotated_x[c];
462 projection->
min_x = INFINITY;
463 projection->
max_x = -INFINITY;
464 projection->
min_y = INFINITY;
465 projection->
max_y = -INFINITY;
474 for(
int corner = 0; corner < 8; corner++)
476 const dt_aligned_pixel_simd_t centered = {
477 ((corner & 1) ? 1.f : 0.f) - 0.5f,
478 ((corner & 2) ? 1.f : 0.f) - 0.5f,
479 ((corner & 4) ? 1.f : 0.f) - 0.5f,
487 projection->
min_x = fminf(projection->
min_x, px);
488 projection->
max_x = fmaxf(projection->
max_x, px);
489 projection->
min_y = fminf(projection->
min_y, py);
490 projection->
max_y = fmaxf(projection->
max_y, py);
495 const float span_x = fmaxf(projection->
max_x - projection->
min_x, 1e-3f);
496 const float span_y = fmaxf(projection->
max_y - projection->
min_y, 1e-3f);
500 projection->
scale = 0.9f * CLAMP(zoom, 0.25f, 8.f) * fminf(available_width / span_x, available_height / span_y);
504 + CLAMP(slice_depth, 0.f, 100.f) * 0.01f
511 float *
const x,
float *
const y,
float *
const depth)
513 const dt_aligned_pixel_simd_t centered = {
rgb[0] - 0.5f,
rgb[1] - 0.5f,
rgb[2] - 0.5f, 0.f };
522static inline void _draw_arrow(cairo_t *cr,
const float x0,
const float y0,
const float x1,
const float y1,
523 const float radius0,
const float radius1,
const dt_aligned_pixel_simd_t
color)
525 const float dx = x1 - x0;
526 const float dy = y1 - y0;
527 const float length = sqrtf(dx * dx + dy * dy);
528 if(length < 1e-3f)
return;
530 const float ux = dx / length;
531 const float uy = dy / length;
532 const float start_x = x0 + radius0 * ux;
533 const float start_y = y0 + radius0 * uy;
534 const float end_x = x1 - radius1 * ux;
535 const float end_y = y1 - radius1 * uy;
536 const float visible_dx = end_x - start_x;
537 const float visible_dy = end_y - start_y;
538 const float visible_length = sqrtf(visible_dx * visible_dx + visible_dy * visible_dy);
539 if(visible_length < 1e-3f)
return;
543 cairo_move_to(cr, start_x, start_y);
544 cairo_line_to(cr, end_x, end_y);
548 const float nx = -uy;
551 cairo_move_to(cr, end_x, end_y);
552 cairo_line_to(cr, end_x - head * ux + 0.5f * head * nx, end_y - head * uy + 0.5f * head * ny);
553 cairo_line_to(cr, end_x - head * ux - 0.5f * head * nx, end_y - head * uy - 0.5f * head * ny);
554 cairo_close_path(cr);
561 float x[8] = { 0.f };
562 float y[8] = { 0.f };
565 for(
int corner = 0; corner < 8; corner++)
567 const dt_aligned_pixel_simd_t
rgb = {
568 (float)((corner & 1) != 0),
569 (
float)((corner & 2) != 0),
570 (float)((corner & 4) != 0),
579 for(
int corner = 0; corner < 8; corner++)
581 for(
int axis = 0; axis < 3; axis++)
583 const int other = corner ^ (1 << axis);
584 if(other < corner)
continue;
586 cairo_move_to(cr,
x[corner], y[corner]);
587 cairo_line_to(cr,
x[other], y[other]);
592 const dt_aligned_pixel_simd_t black = { 0.f, 0.f, 0.f, 0.f };
593 const dt_aligned_pixel_simd_t white = { 1.f, 1.f, 1.f, 0.f };
594 float x0 = 0.f, y0 = 0.f;
595 float x1 = 0.f, y1 = 0.f;
596 dt_aligned_pixel_simd_t display_rgb = { 0.f };
597 cairo_pattern_t *gradient = NULL;
606 gradient = cairo_pattern_create_linear(x0, y0, x1, y1);
607 for(
int k = 0;
k <= 16;
k++)
609 const float grey = (float)
k / 16.f;
610 display_rgb =
_to_display_rgb(viewer, (dt_aligned_pixel_simd_t){ grey, grey, grey, 0.f });
611 cairo_pattern_add_color_stop_rgba(gradient, grey, display_rgb[0], display_rgb[1], display_rgb[2], 0.85);
614 cairo_set_source(cr, gradient);
616 cairo_move_to(cr, x0, y0);
617 cairo_line_to(cr, x1, y1);
619 cairo_pattern_destroy(gradient);
624 static const dt_aligned_pixel_simd_t center = { 0.5f, 0.5f, 0.5f, 0.f };
628 const dt_aligned_pixel_simd_t axis_work[3] = {
629 { 1.f, 0.f, 0.f, 0.f },
630 { 0.f, 1.f, 0.f, 0.f },
631 { 0.f, 0.f, 1.f, 0.f }
633 dt_aligned_pixel_simd_t axis_display[3] = { { 0.f }, { 0.f }, { 0.f } };
634 float cx = 0.f, cy = 0.f, depth = 0.f;
635 float px = 0.f, py = 0.f;
641 cairo_set_source_rgba(cr, axis_display[0][0], axis_display[0][1], axis_display[0][2], 0.9);
643 cairo_move_to(cr, cx, cy);
644 cairo_line_to(cr, px, py);
648 cairo_set_source_rgba(cr, axis_display[1][0], axis_display[1][1], axis_display[1][2], 0.9);
649 cairo_move_to(cr, cx, cy);
650 cairo_line_to(cr, px, py);
654 cairo_set_source_rgba(cr, axis_display[2][0], axis_display[2][1], axis_display[2][2], 0.9);
655 cairo_move_to(cr, cx, cy);
656 cairo_line_to(cr, px, py);
664 while(((level + stride - 1) / stride) * ((level + stride - 1) / stride) * ((level + stride - 1) / stride)
673 return MAX((level + stride - 1) / stride, 2);
676static inline int _sample_index(
const int sample,
const int samples,
const int level)
678 if(samples <= 1)
return 0;
679 return MIN((
int)lroundf((
float)sample * (
float)(level - 1) / (
float)(samples - 1)), level - 1);
687 if(show_control_nodes)
696 const double total_start = log_perf ?
dt_get_wtime() : 0.0;
697 double collect_done = 0.0;
698 double convert_done = 0.0;
707 : (size_t)samples * (
size_t)samples * (size_t)samples;
713 const float zoom_scale = sqrtf(fmaxf(viewer->
zoom, 0.25f));
716 const gboolean rebuild_sample_cache
732 if(rebuild_sample_cache)
740 if(show_control_nodes)
744 const dt_aligned_pixel_simd_t input_rgb = {
750 const dt_aligned_pixel_simd_t output_rgb = {
758 float x0 = 0.f, y0 = 0.f, depth0 = 0.f;
759 float x1 = 0.f, y1 = 0.f, depth1 = 0.f;
764 const gboolean destination_in_slice
766 if(!target_in_slice && !destination_in_slice)
continue;
771 if(input_rgb[0] >= 1.f - 1e-6f && input_rgb[1] >= 1.f - 1e-6f && input_rgb[2] >= 1.f - 1e-6f)
794 for(
int sample_b = 0; sample_b < samples; sample_b++)
795 for(
int sample_g = 0; sample_g < samples; sample_g++)
796 for(
int sample_r = 0; sample_r < samples; sample_r++)
801 const dt_aligned_pixel_simd_t input_rgb = {
811 const size_t index = (size_t)(
r +
g * viewer->
clut_level + b * level2) * 3;
812 const dt_aligned_pixel_simd_t output_rgb = {
813 CLAMP(viewer->
clut[index + 0], 0.f, 1.f),
814 CLAMP(viewer->
clut[index + 1], 0.f, 1.f),
815 CLAMP(viewer->
clut[index + 2], 0.f, 1.f),
820 float x0 = 0.f, y0 = 0.f, depth0 = 0.f;
821 float x1 = 0.f, y1 = 0.f, depth1 = 0.f;
825 const gboolean target_in_slice
827 const gboolean destination_in_slice
829 if(!target_in_slice && !destination_in_slice)
continue;
846 "lut viewer swatch inputs");
848 "lut viewer swatch outputs");
868 collect_done = total_start;
869 convert_done = total_start;
876 float x0 = 0.f, y0 = 0.f, depth0 = 0.f;
877 float x1 = 0.f, y1 = 0.f, depth1 = 0.f;
883 cairo_arc(cr, x0, y0, target_radius, 0.f, 2.f *
M_PI_F);
886 cairo_fill_preserve(cr);
887 cairo_set_source_rgba(cr, 0.1, 0.1, 0.1, 0.6);
890 cairo_arc(cr, x1, y1, destination_radius, 0.f, 2.f *
M_PI_F);
893 cairo_fill_preserve(cr);
894 cairo_set_source_rgba(cr, 1.0, 1.0, 1.0, 0.5);
901 float x0 = 0.f, y0 = 0.f, depth0 = 0.f;
902 float x1 = 0.f, y1 = 0.f, depth1 = 0.f;
908 cairo_arc(cr, x0, y0, target_radius, 0.f, 2.f *
M_PI_F);
911 cairo_fill_preserve(cr);
912 cairo_set_source_rgba(cr, 0.2, 0.2, 0.2, 0.8);
915 cairo_arc(cr, x1, y1, destination_radius, 0.f, 2.f *
M_PI_F);
918 cairo_fill_preserve(cr);
919 cairo_set_source_rgba(cr, 0.2, 0.2, 0.2, 0.8);
927 "[lut_viewer] draw_samples mode=%s level=%u sparse=%d^3 max=%" G_GSIZE_FORMAT
" drawn=%" G_GSIZE_FORMAT
" gamut=%d cache=%s collect=%.3fms convert=%.3fms paint=%.3fms total=%.3fms\n",
928 show_control_nodes ?
"controls" :
"lut", viewer->
clut_level, samples, max_samples, viewer->
sample_count, gamut,
929 rebuild_sample_cache ?
"rebuild" :
"reuse",
930 1000.0 * (collect_done - total_start),
931 1000.0 * (convert_done - collect_done),
932 1000.0 * (total_done - convert_done),
933 1000.0 * (total_done - total_start));
939 cairo_text_extents_t extents;
940 cairo_select_font_face(cr,
"sans", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL);
942 cairo_text_extents(cr, message, &extents);
943 cairo_set_source_rgba(cr, 0.9, 0.9, 0.9, 0.7);
944 cairo_move_to(cr, 0.5f * ((
float)
width - extents.width), 0.5f * ((
float)
height - extents.height));
945 cairo_show_text(cr, message);
956 MAX((
int)ceil((
double)
width * ppd), 1),
957 MAX((
int)ceil((
double)
height * ppd), 1));
958 cairo_surface_set_device_scale(viewer->
surface, ppd, ppd);
959 cairo_t *cr = cairo_create(viewer->
surface);
960 GtkStyleContext *context = gtk_widget_get_style_context(GTK_WIDGET(viewer->
area));
962 gtk_render_background(context, cr, 0, 0,
width,
height);
963 gtk_render_frame(context, cr, 0, 0,
width,
height);
972 "[lut_viewer] render_surface %dx%d ppd=%.2f placeholder=1 total=%.3fms\n",
994 "[lut_viewer] render_surface %dx%d ppd=%.2f placeholder=1 total=%.3fms\n",
1020 "[lut_viewer] render_surface %dx%d ppd=%.2f level=%u gamut=%d total=%.3fms\n",
1027 GtkAllocation allocation;
1028 gtk_widget_get_allocation(widget, &allocation);
1035 const float zoom = viewer->
zoom;
1085 cairo_set_source_surface(cr, viewer->
surface, 0, 0);
1096 gtk_widget_queue_draw(GTK_WIDGET(viewer->
area));
1102 GtkAllocation allocation;
1103 gtk_widget_get_allocation(widget, &allocation);
1104 float zoom_step = 1.f;
1106 switch(event->direction)
1111 case GDK_SCROLL_DOWN:
1112 zoom_step = 1.f / 1.1f;
1114 case GDK_SCROLL_SMOOTH:
1115 zoom_step = exp2f((
float)(-event->delta_y) * 0.2f);
1121 const float old_zoom = viewer->
zoom;
1122 const float new_zoom = CLAMP(old_zoom * zoom_step, 0.25f, 8.f);
1123 const float zoom_ratio = new_zoom / old_zoom;
1124 const float center_x = 0.5f * allocation.width;
1125 const float center_y = 0.5f * allocation.height;
1127 viewer->
pan_x =
event->x - center_x - zoom_ratio * (
event->x - center_x - viewer->
pan_x);
1128 viewer->
pan_y =
event->y - center_y - zoom_ratio * (
event->y - center_y - viewer->
pan_y);
1129 viewer->
zoom = new_zoom;
1131 gtk_widget_queue_draw(widget);
1138 if(event->type == GDK_2BUTTON_PRESS)
1145 viewer->
pan_x = 0.f;
1146 viewer->
pan_y = 0.f;
1149 gtk_widget_queue_draw(widget);
1153 if(event->button == 1)
1155 else if(event->button == 2)
1179 gtk_widget_queue_draw(widget);
1186 - 0.25f * (
float)(event->y - viewer->
drag_anchor_y), 0.f, 90.f);
1213 GtkFileChooserNative *filechooser = gtk_file_chooser_native_new(
1214 _(
"save 3D LUT"), GTK_WINDOW(win), GTK_FILE_CHOOSER_ACTION_SAVE,
1215 _(
"_save"), _(
"_cancel"));
1216 gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(filechooser),
FALSE);
1217 gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(filechooser),
TRUE);
1218 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(filechooser),
"lut-viewer-export.cube");
1222 gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(filechooser), lutfolder);
1226 GtkFileFilter *filter = GTK_FILE_FILTER(gtk_file_filter_new());
1227 gtk_file_filter_add_pattern(filter,
"*.cube");
1228 gtk_file_filter_add_pattern(filter,
"*.CUBE");
1229 gtk_file_filter_set_name(filter, _(
"3D lut (cube)"));
1230 gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(filechooser), filter);
1231 gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(filechooser), filter);
1233 if(gtk_native_dialog_run(GTK_NATIVE_DIALOG(filechooser)) == GTK_RESPONSE_ACCEPT)
1235 gchar *filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(filechooser));
1236 char *path = filename;
1237 FILE *cube_file = NULL;
1239 if(!g_str_has_suffix(filename,
".cube") && !g_str_has_suffix(filename,
".CUBE"))
1240 path = g_strconcat(filename,
".cube", NULL);
1242 cube_file = g_fopen(path,
"wb");
1249 const char *
const lut_profile_name
1252 fprintf(cube_file,
"# generated by ansel LUT viewer\n");
1253 fprintf(cube_file,
"# LUT_COLORSPACE %s\n", lut_profile_name);
1254 fprintf(cube_file,
"TITLE \"lut viewer export\"\n");
1255 fprintf(cube_file,
"LUT_3D_SIZE %u\n", viewer->
clut_level);
1256 fprintf(cube_file,
"DOMAIN_MIN 0.0 0.0 0.0\n");
1257 fprintf(cube_file,
"DOMAIN_MAX 1.0 1.0 1.0\n");
1259 for(uint16_t b = 0; b < viewer->
clut_level; b++)
1265 fprintf(cube_file,
"%.7f %.7f %.7f\n",
1266 viewer->
clut[index + 0], viewer->
clut[index + 1], viewer->
clut[index + 2]);
1274 if(path != filename) g_free(path);
1279 g_object_unref(filechooser);
1291 gtk_widget_add_events(GTK_WIDGET(viewer->
area),
1292 GDK_SCROLL_MASK | GDK_SMOOTH_SCROLL_MASK | GDK_BUTTON_PRESS_MASK
1293 | GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK);
1294 g_signal_connect(G_OBJECT(viewer->
area),
"draw", G_CALLBACK(
_draw_callback), viewer);
1295 g_signal_connect(G_OBJECT(viewer->
area),
"scroll-event", G_CALLBACK(
_scroll_callback), viewer);
1299 gtk_box_pack_start(GTK_BOX(viewer->
widget), GTK_WIDGET(viewer->
area),
TRUE,
TRUE, 0);
1336 viewer->
show_control_nodes = gtk_check_button_new_with_label(_(
"show control nodes"));
1351 viewer->
save_button = gtk_button_new_with_label(_(
"save to cLUT"));
1355 GtkWidget *expander = gtk_expander_new(_(
"viewer controls"));
1356 gtk_expander_set_expanded(GTK_EXPANDER(expander),
FALSE);
1357 gtk_container_add(GTK_CONTAINER(expander), viewer->
controls);
1361 viewer->
pan_x = 0.f;
1362 viewer->
pan_y = 0.f;
1401 return viewer ? viewer->
widget : NULL;
1405 dt_pthread_rwlock_t *clut_lock,
1411 viewer->
clut = clut;
1422 size_t control_node_count)
1431 const gboolean
enabled = control_nodes && control_node_count > 0;
1442 if(viewer) gtk_widget_queue_draw(GTK_WIDGET(viewer->
area));
GtkWidget * dt_gui_main_window(void)
void dt_bauhaus_slider_reset(GtkWidget *widget)
float dt_bauhaus_slider_get(GtkWidget *widget)
int dt_bauhaus_combobox_get(GtkWidget *widget)
void dt_bauhaus_slider_set(GtkWidget *widget, float pos)
void dt_bauhaus_combobox_set(GtkWidget *widget, const int pos)
void dt_bauhaus_widget_set_label(GtkWidget *widget, const char *label)
GtkWidget * dt_bauhaus_slider_new_with_range(dt_bauhaus_t *bh, dt_gui_module_t *self, float min, float max, float step, float defval, int digits)
GtkWidget * dt_bauhaus_combobox_new(dt_bauhaus_t *bh, dt_gui_module_t *self)
void dt_bauhaus_slider_set_format(GtkWidget *widget, const char *format)
void dt_bauhaus_combobox_add(GtkWidget *widget, const char *text)
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)
const dt_colorspaces_color_profile_t * dt_colorspaces_get_profile(dt_colorspaces_color_profile_type_t type, const char *filename, dt_colorspaces_profile_role_t role)
Resolve a profile identity to its registered entry.
const char * dt_colorspaces_get_name(dt_colorspaces_color_profile_type_t type, const char *filename)
Printable name for a profile identity, without touching the profile list.
The colour-profile module's API: which profiles exist, and how to apply one.
struct _GtkWidget GtkWidget
GtkWidget, opaque, spelled exactly as GTK spells it.
static dt_aligned_pixel_t rgb
dt_apply_transposed_color_matrix(XYZ, xyz_to_srgb_matrix_transposed, sRGB)
const dt_colormatrix_t dt_aligned_pixel_t out
gchar * dt_conf_get_string(const char *name)
Read the stored string for name as a private copy.
void dt_conf_set_folder_from_file_chooser(const char *name, GtkFileChooser *chooser)
gboolean dt_conf_get_folder_to_file_chooser(const char *name, GtkFileChooser *chooser)
void dt_control_log(const char *msg,...)
struct dt_gui_gtk_t * dt_gui_get_global(void)
struct dt_bauhaus_t * dt_bauhaus_get_global(void)
gboolean enabled
–doc was passed on the command line
static void set_color(cairo_t *cr, GdkRGBA color)
GtkWidget * dtgtk_drawing_area_new_with_aspect_ratio(double aspect)
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
int32_t dt_get_debug_flags(void)
void dt_print(dt_debug_thread_t thread, const char *msg,...) __attribute__((format(printf
Print to stdout when thread is enabled, prefixed with seconds since startup.
float *const restrict const size_t k
static void _control_changed(GtkWidget *widget, gpointer user_data)
static gboolean _show_control_nodes(const dt_lut_viewer_t *viewer)
static float _wrap_degrees_pm180(float angle)
static gboolean _button_press_callback(GtkWidget *widget, GdkEventButton *event, gpointer user_data)
static dt_aligned_pixel_simd_t _normalize3(const dt_aligned_pixel_simd_t vector)
static dt_aligned_pixel_simd_t _set_vector(const float x, const float y, const float z)
static void _save_clut_callback(GtkWidget *widget, gpointer user_data)
static int _get_xyz_to_rgb_matrix(const dt_lut_viewer_gamut_t gamut, dt_colormatrix_t xyz_to_rgb)
@ DT_LUT_VIEWER_GAMUT_REC2020
@ DT_LUT_VIEWER_GAMUT_ADOBE_RGB
@ DT_LUT_VIEWER_GAMUT_SRGB
@ DT_LUT_VIEWER_GAMUT_DISPLAY_P3
static void _project_point(const dt_lut_viewer_projection_t *projection, const dt_aligned_pixel_simd_t rgb, float *const x, float *const y, float *const depth)
void dt_lut_viewer_destroy(dt_lut_viewer_t **viewer)
static gboolean _button_release_callback(GtkWidget *widget, GdkEventButton *event, gpointer user_data)
static int _sample_stride(const int level)
static float _shift_distance_percent(const dt_aligned_pixel_simd_t input_rgb, const dt_aligned_pixel_simd_t output_rgb)
static gboolean _scroll_callback(GtkWidget *widget, GdkEventScroll *event, gpointer user_data)
static void _render_surface(dt_lut_viewer_t *viewer, const int width, const int height)
static void _clamp_display_rgb_array_simd(const dt_aligned_pixel_simd_t *source_rgb, dt_aligned_pixel_simd_t *display_rgb, const size_t count)
void dt_lut_viewer_queue_draw(dt_lut_viewer_t *viewer)
static dt_aligned_pixel_simd_t _to_display_rgb(const dt_lut_viewer_t *viewer, const dt_aligned_pixel_simd_t work_rgb)
static dt_aligned_pixel_simd_t _clamp01_simd(const dt_aligned_pixel_simd_t value)
static int _sample_count(const int level, const int stride)
static gboolean _sample_fits_gamut(const dt_iop_order_iccprofile_info_t *lut_profile, const dt_colormatrix_t xyz_to_rgb, const dt_aligned_pixel_simd_t rgb)
void dt_lut_viewer_set_control_nodes(dt_lut_viewer_t *viewer, const dt_lut_viewer_control_node_t *control_nodes, size_t control_node_count)
static gboolean _draw_callback(GtkWidget *widget, cairo_t *cr, gpointer user_data)
static dt_colorspaces_color_profile_type_t _gamut_to_profile_type(const dt_lut_viewer_gamut_t gamut)
GtkWidget * dt_lut_viewer_get_widget(dt_lut_viewer_t *viewer)
dt_lut_viewer_t * dt_lut_viewer_new(dt_gui_module_t *module)
static void _draw_samples(cairo_t *cr, const dt_lut_viewer_t *viewer, const dt_lut_viewer_projection_t *projection, const dt_lut_viewer_gamut_t gamut)
static void _to_display_rgb_array(const dt_lut_viewer_t *viewer, const dt_aligned_pixel_simd_t *work_rgb, dt_aligned_pixel_simd_t *display_rgb, const size_t count, const char *message)
#define DT_LUT_VIEWER_TARGET_SAMPLES
dt_lut_viewer_drag_mode_t
@ DT_LUT_VIEWER_DRAG_ORBIT
@ DT_LUT_VIEWER_DRAG_NONE
static dt_aligned_pixel_simd_t _transform_single_rgb_matrix(const dt_aligned_pixel_simd_t input, const dt_iop_order_iccprofile_info_t *const profile_info_from, const dt_iop_order_iccprofile_info_t *const profile_info_to)
static int _sample_index(const int sample, const int samples, const int level)
static void _draw_axes(cairo_t *cr, const dt_lut_viewer_t *viewer, const dt_lut_viewer_projection_t *projection)
static gboolean _motion_notify_callback(GtkWidget *widget, GdkEventMotion *event, gpointer user_data)
static void _draw_placeholder(cairo_t *cr, const int width, const int height, const char *message)
static void _invalidate_sample_cache(dt_lut_viewer_t *viewer)
static gboolean _gamut_matches_lut_profile(const dt_lut_viewer_t *viewer, const dt_lut_viewer_gamut_t gamut)
#define DT_LUT_VIEWER_MARGIN
static dt_aligned_pixel_simd_t _cross3(const dt_aligned_pixel_simd_t a, const dt_aligned_pixel_simd_t b)
static void _build_projection(dt_lut_viewer_projection_t *projection, const float rotation_around_axis, const float rotation_of_axis, const float slice_depth, const float slice_thickness, const float zoom, const float pan_x, const float pan_y, const int width, const int height)
static void _draw_arrow(cairo_t *cr, const float x0, const float y0, const float x1, const float y1, const float radius0, const float radius1, const dt_aligned_pixel_simd_t color)
static int _ensure_sample_cache_capacity(dt_lut_viewer_t *viewer, const size_t capacity)
#define DT_LUT_VIEWER_AXIS_LENGTH
static float _dot3(const dt_aligned_pixel_simd_t a, const dt_aligned_pixel_simd_t b)
void dt_lut_viewer_set_lut(dt_lut_viewer_t *viewer, const float *clut, uint16_t level, dt_pthread_rwlock_t *clut_lock, const dt_iop_order_iccprofile_info_t *lut_profile, const dt_iop_order_iccprofile_info_t *display_profile)
static void _invalidate_surface(dt_lut_viewer_t *viewer)
static void _draw_cube(cairo_t *cr, const dt_lut_viewer_t *viewer, const dt_lut_viewer_projection_t *projection)
#define IS_NULL_PTR(p)
C is way too permissive with !=, == and if(var) checks, which can mean too many things depending on w...
float DT_ALIGNED_ARRAY dt_colormatrix_t[4][4]
static int mat3SSEinv(dt_colormatrix_t dst, const dt_colormatrix_t src)
#define dt_free_align(ptr)
Release memory from dt_alloc_align() and set ptr to NULL.
static void * dt_calloc_align(size_t size)
dt_alloc_align() followed by a zero fill.
#define dt_free(ptr)
g_free() ptr and set it to NULL, skipping both if it is already NULL.
#define __OMP_SIMD__(...)
dt_colorspaces_color_profile_type_t
@ DT_COLORSPACE_SRGB
sRGB – registered TWICE, and the two entries are not interchangeable.
@ DT_COLORSPACE_PQ_REC2020
@ DT_COLORSPACE_HLG_REC2020
@ DT_COLORSPACE_LIN_REC2020
@ DT_COLORSPACE_DISPLAY_P3
@ DT_COLORSPACE_LIN_REC709
@ DT_PROFILE_ROLE_ANY
All four roles, in registration order.
static cairo_surface_t * dt_cairo_image_surface_create(cairo_format_t format, int width, int height)
DT_ALIGNED_PIXEL float dt_aligned_pixel_t[4]
static const dt_aligned_pixel_simd_t value
One registered profile: its identity, its LCMS handle, and where it sits in each combo box.
cmsHPROFILE profile
the actual profile; NULL for the three category entries
The dt_gui_module_t type is the intersection between a dt_lib_module_t and a dt_iop_module_t structur...
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
dt_colorspaces_color_profile_type_t type
Profile identity, half of the memo key. DT_COLORSPACE_NONE means "unset" and makes every apply functi...
float * lut_out[3]
Per-channel linear -> encoded tone curve, same convention as lut_in.
dt_colormatrix_t matrix_out
XYZ (D50) -> RGB, row-major; the inverse of matrix_in.
float * lut_in[3]
Per-channel encoded -> linear tone curve, lutsize entries each, sampled over [0,1]....
char filename[DT_IOP_COLOR_ICC_LEN]
ICC file name, the other half of the memo key; "" for every built-in. Compared with strcmp,...
dt_colormatrix_t matrix_in_transposed
dt_colormatrix_t matrix_in
RGB -> XYZ (D50), row-major. matrix_in[1][*] is the luminance row. NaN in [0][0] marks the whole prof...
float slice_half_thickness
dt_aligned_pixel_simd_t screen_x
dt_aligned_pixel_simd_t screen_z
dt_aligned_pixel_simd_t screen_y
dt_pthread_rwlock_t * clut_lock
float sample_cache_rotation_of_axis
float cached_rotation_around_axis
const dt_iop_order_iccprofile_info_t * display_profile
float cached_slice_thickness
const dt_iop_order_iccprofile_info_t * cached_display_profile
float sample_cache_slice_thickness
float cached_rotation_of_axis
float drag_origin_azimuth
float sample_cache_slice_depth
gboolean cached_show_control_nodes
gboolean sample_cache_show_control_nodes
dt_aligned_pixel_simd_t * sample_input_display
dt_aligned_pixel_simd_t * sample_output_display
const dt_iop_order_iccprofile_info_t * lut_profile
const float * cached_clut
dt_aligned_pixel_simd_t * sample_input_work
const float * sample_cache_clut
uint16_t sample_cache_clut_level
float cached_shift_threshold
uint16_t cached_clut_level
cairo_surface_t * surface
const dt_lut_viewer_control_node_t * control_nodes
size_t cached_control_node_count
size_t sample_white_index
float sample_cache_rotation_around_axis
const dt_iop_order_iccprofile_info_t * sample_cache_display_profile
GtkWidget * slice_thickness
gboolean sample_cache_valid
const dt_iop_order_iccprofile_info_t * sample_cache_lut_profile
GtkWidget * rotation_of_axis
float sample_cache_shift_threshold
const dt_lut_viewer_control_node_t * cached_control_nodes
gboolean sample_draw_white_last
dt_lut_viewer_drag_mode_t drag_mode
const dt_iop_order_iccprofile_info_t * cached_lut_profile
dt_aligned_pixel_simd_t * sample_output_work
size_t sample_cache_control_node_count
GtkWidget * rotation_around_axis
const dt_lut_viewer_control_node_t * sample_cache_control_nodes
GtkWidget * show_control_nodes
GtkWidget * shift_threshold
size_t control_node_count
static double dt_get_wtime(void)
Telling the user something happened.