35#define DT_LUT_VIEWER_MARGIN DT_PIXEL_APPLY_DPI(12)
36#define DT_LUT_VIEWER_TARGET_SAMPLES 4096
37#define DT_LUT_VIEWER_AXIS_LENGTH DT_PIXEL_APPLY_DPI(20.f)
149static inline dt_aligned_pixel_simd_t
_set_vector(
const float x,
const float y,
const float z)
151 return (dt_aligned_pixel_simd_t){
x, y, z, 0.f };
154static inline float _dot3(
const dt_aligned_pixel_simd_t a,
const dt_aligned_pixel_simd_t b)
156 return a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
159static inline dt_aligned_pixel_simd_t
_cross3(
const dt_aligned_pixel_simd_t a,
const dt_aligned_pixel_simd_t b)
162 a[2] * b[0] - a[0] * b[2],
163 a[0] * b[1] - a[1] * b[0]);
166static inline dt_aligned_pixel_simd_t
_normalize3(
const dt_aligned_pixel_simd_t vector)
168 const float norm = sqrtf(
_dot3(vector, vector));
169 if(norm < 1e-6f)
return vector;
171 const float inv_norm = 1.f / norm;
172 return (dt_aligned_pixel_simd_t){ vector[0] * inv_norm, vector[1] * inv_norm, vector[2] * inv_norm, 0.f };
177 while(angle <= -180.f) angle += 360.f;
178 while(angle > 180.f) angle -= 360.f;
183 const dt_aligned_pixel_simd_t output_rgb)
185 const float dr = output_rgb[0] - input_rgb[0];
186 const float dg = output_rgb[1] - input_rgb[1];
187 const float db = output_rgb[2] - input_rgb[2];
188 return 100.f * sqrtf((dr * dr + dg * dg + db * db) / 3.f);
193 dt_aligned_pixel_simd_t
out = dt_simd_max_zero(
value);
195 for(
int c = 0; c < 3; c++)
196 out[c] = fminf(
out[c], 1.f);
208 dt_aligned_pixel_simd_t *display_rgb,
const size_t count)
210 if(
IS_NULL_PTR(source_rgb) || !display_rgb || count == 0)
return;
212 for(
size_t k = 0;
k < count;
k++)
223 const dt_aligned_pixel_simd_t input,
231 profile_info_from->
lut_in, profile_info_from->unbounded_coeffs_in,
238 _apply_trc(linear_rgb, output, profile_info_to->
lut_out, profile_info_to->unbounded_coeffs_out,
248 const dt_aligned_pixel_simd_t work_rgb)
250 dt_aligned_pixel_simd_t display_rgb =
_clamp01_simd(work_rgb);
258 dt_aligned_pixel_simd_t in = work_rgb;
259 dt_aligned_pixel_simd_t
out = work_rgb;
261 "lut viewer swatch");
267 dt_aligned_pixel_simd_t *display_rgb,
const size_t count,
const char *message)
269 if(
IS_NULL_PTR(work_rgb) || !display_rgb || count == 0)
return;
283 cairo_surface_destroy(viewer->
surface);
390 const cmsCIEXYZ *red = cmsReadTag(profile->
profile, cmsSigRedColorantTag);
391 const cmsCIEXYZ *green = cmsReadTag(profile->
profile, cmsSigGreenColorantTag);
392 const cmsCIEXYZ *blue = cmsReadTag(profile->
profile, cmsSigBlueColorantTag);
396 rgb_to_xyz[0][0] = red->X;
397 rgb_to_xyz[0][1] = green->X;
398 rgb_to_xyz[0][2] = blue->X;
399 rgb_to_xyz[1][0] = red->Y;
400 rgb_to_xyz[1][1] = green->Y;
401 rgb_to_xyz[1][2] = blue->Y;
402 rgb_to_xyz[2][0] = red->Z;
403 rgb_to_xyz[2][1] = green->Z;
404 rgb_to_xyz[2][2] = blue->Z;
411 const dt_aligned_pixel_simd_t
rgb)
413 const float epsilon = 1e-3f;
414 dt_aligned_pixel_simd_t xyz = { 0.f };
415 dt_aligned_pixel_simd_t gamut_rgb = { 0.f };
417 dot_product((
float *)&xyz, xyz_to_rgb, (
float *)&gamut_rgb);
425 return gamut_rgb[0] >= -epsilon && gamut_rgb[0] <= 1.f + epsilon
426 && gamut_rgb[1] >= -epsilon && gamut_rgb[1] <= 1.f + epsilon
427 && gamut_rgb[2] >= -epsilon && gamut_rgb[2] <= 1.f + epsilon;
431 const float rotation_of_axis,
const float slice_depth,
const float slice_thickness,
433 const float pan_x,
const float pan_y,
const int width,
const int height)
435 static const dt_aligned_pixel_simd_t axis = { 0.5773502691896258f, 0.5773502691896258f, 0.5773502691896258f, 0.f };
436 static const dt_aligned_pixel_simd_t chroma_x = { 0.7071067811865475f, -0.7071067811865475f, 0.f, 0.f };
437 static const dt_aligned_pixel_simd_t chroma_y = { 0.4082482904638630f, 0.4082482904638630f, -0.8164965809277260f, 0.f };
439 const float azimuth = rotation_around_axis *
M_PI_F / 180.f;
440 const float tilt = rotation_of_axis *
M_PI_F / 180.f;
441 dt_aligned_pixel_simd_t rotated_x = { 0.f };
442 dt_aligned_pixel_simd_t rotated_y = { 0.f };
444 for(
int c = 0; c < 3; c++)
446 rotated_x[c] = cosf(azimuth) * chroma_x[c] + sinf(azimuth) * chroma_y[c];
447 rotated_y[c] = -sinf(azimuth) * chroma_x[c] + cosf(azimuth) * chroma_y[c];
448 projection->
screen_z[c] = cosf(tilt) * rotated_y[c] + sinf(tilt) * axis[c];
449 projection->
screen_x[c] = rotated_x[c];
455 projection->
min_x = INFINITY;
456 projection->
max_x = -INFINITY;
457 projection->
min_y = INFINITY;
458 projection->
max_y = -INFINITY;
467 for(
int corner = 0; corner < 8; corner++)
469 const dt_aligned_pixel_simd_t centered = {
470 ((corner & 1) ? 1.f : 0.f) - 0.5f,
471 ((corner & 2) ? 1.f : 0.f) - 0.5f,
472 ((corner & 4) ? 1.f : 0.f) - 0.5f,
480 projection->
min_x = fminf(projection->
min_x, px);
481 projection->
max_x = fmaxf(projection->
max_x, px);
482 projection->
min_y = fminf(projection->
min_y, py);
483 projection->
max_y = fmaxf(projection->
max_y, py);
488 const float span_x = fmaxf(projection->
max_x - projection->
min_x, 1e-3f);
489 const float span_y = fmaxf(projection->
max_y - projection->
min_y, 1e-3f);
493 projection->
scale = 0.9f * CLAMP(zoom, 0.25f, 8.f) * fminf(available_width / span_x, available_height / span_y);
497 + CLAMP(slice_depth, 0.f, 100.f) * 0.01f
504 float *
const x,
float *
const y,
float *
const depth)
506 const dt_aligned_pixel_simd_t centered = {
rgb[0] - 0.5f,
rgb[1] - 0.5f,
rgb[2] - 0.5f, 0.f };
515static inline void _draw_arrow(cairo_t *cr,
const float x0,
const float y0,
const float x1,
const float y1,
516 const float radius0,
const float radius1,
const dt_aligned_pixel_simd_t color)
518 const float dx = x1 - x0;
519 const float dy = y1 - y0;
520 const float length = sqrtf(dx * dx + dy * dy);
521 if(length < 1e-3f)
return;
523 const float ux = dx / length;
524 const float uy = dy / length;
525 const float start_x = x0 + radius0 * ux;
526 const float start_y = y0 + radius0 * uy;
527 const float end_x = x1 - radius1 * ux;
528 const float end_y = y1 - radius1 * uy;
529 const float visible_dx = end_x - start_x;
530 const float visible_dy = end_y - start_y;
531 const float visible_length = sqrtf(visible_dx * visible_dx + visible_dy * visible_dy);
532 if(visible_length < 1e-3f)
return;
534 cairo_set_source_rgba(cr, color[0], color[1], color[2], 0.65);
536 cairo_move_to(cr, start_x, start_y);
537 cairo_line_to(cr, end_x, end_y);
541 const float nx = -uy;
544 cairo_move_to(cr, end_x, end_y);
545 cairo_line_to(cr, end_x - head * ux + 0.5f * head * nx, end_y - head * uy + 0.5f * head * ny);
546 cairo_line_to(cr, end_x - head * ux - 0.5f * head * nx, end_y - head * uy - 0.5f * head * ny);
547 cairo_close_path(cr);
548 cairo_set_source_rgba(cr, color[0], color[1], color[2], 0.75);
554 float x[8] = { 0.f };
555 float y[8] = { 0.f };
558 for(
int corner = 0; corner < 8; corner++)
560 const dt_aligned_pixel_simd_t
rgb = {
561 (float)((corner & 1) != 0),
562 (
float)((corner & 2) != 0),
563 (float)((corner & 4) != 0),
572 for(
int corner = 0; corner < 8; corner++)
574 for(
int axis = 0; axis < 3; axis++)
576 const int other = corner ^ (1 << axis);
577 if(other < corner)
continue;
579 cairo_move_to(cr,
x[corner], y[corner]);
580 cairo_line_to(cr,
x[other], y[other]);
585 const dt_aligned_pixel_simd_t black = { 0.f, 0.f, 0.f, 0.f };
586 const dt_aligned_pixel_simd_t white = { 1.f, 1.f, 1.f, 0.f };
587 float x0 = 0.f, y0 = 0.f;
588 float x1 = 0.f, y1 = 0.f;
589 dt_aligned_pixel_simd_t display_rgb = { 0.f };
590 cairo_pattern_t *gradient = NULL;
599 gradient = cairo_pattern_create_linear(x0, y0, x1, y1);
600 for(
int k = 0;
k <= 16;
k++)
602 const float grey = (float)
k / 16.f;
603 display_rgb =
_to_display_rgb(viewer, (dt_aligned_pixel_simd_t){ grey, grey, grey, 0.f });
604 cairo_pattern_add_color_stop_rgba(gradient, grey, display_rgb[0], display_rgb[1], display_rgb[2], 0.85);
607 cairo_set_source(cr, gradient);
609 cairo_move_to(cr, x0, y0);
610 cairo_line_to(cr, x1, y1);
612 cairo_pattern_destroy(gradient);
617 static const dt_aligned_pixel_simd_t center = { 0.5f, 0.5f, 0.5f, 0.f };
621 const dt_aligned_pixel_simd_t axis_work[3] = {
622 { 1.f, 0.f, 0.f, 0.f },
623 { 0.f, 1.f, 0.f, 0.f },
624 { 0.f, 0.f, 1.f, 0.f }
626 dt_aligned_pixel_simd_t axis_display[3] = { { 0.f }, { 0.f }, { 0.f } };
627 float cx = 0.f, cy = 0.f, depth = 0.f;
628 float px = 0.f, py = 0.f;
634 cairo_set_source_rgba(cr, axis_display[0][0], axis_display[0][1], axis_display[0][2], 0.9);
636 cairo_move_to(cr, cx, cy);
637 cairo_line_to(cr, px, py);
641 cairo_set_source_rgba(cr, axis_display[1][0], axis_display[1][1], axis_display[1][2], 0.9);
642 cairo_move_to(cr, cx, cy);
643 cairo_line_to(cr, px, py);
647 cairo_set_source_rgba(cr, axis_display[2][0], axis_display[2][1], axis_display[2][2], 0.9);
648 cairo_move_to(cr, cx, cy);
649 cairo_line_to(cr, px, py);
657 while(((level + stride - 1) / stride) * ((level + stride - 1) / stride) * ((level + stride - 1) / stride)
666 return MAX((level + stride - 1) / stride, 2);
669static inline int _sample_index(
const int sample,
const int samples,
const int level)
671 if(samples <= 1)
return 0;
672 return MIN((
int)lroundf((
float)sample * (
float)(level - 1) / (
float)(samples - 1)), level - 1);
680 if(show_control_nodes)
689 const double total_start = log_perf ?
dt_get_wtime() : 0.0;
690 double collect_done = 0.0;
691 double convert_done = 0.0;
700 : (size_t)samples * (
size_t)samples * (size_t)samples;
706 const float zoom_scale = sqrtf(fmaxf(viewer->
zoom, 0.25f));
709 const gboolean rebuild_sample_cache
725 if(rebuild_sample_cache)
733 if(show_control_nodes)
737 const dt_aligned_pixel_simd_t input_rgb = {
743 const dt_aligned_pixel_simd_t output_rgb = {
751 float x0 = 0.f, y0 = 0.f, depth0 = 0.f;
752 float x1 = 0.f, y1 = 0.f, depth1 = 0.f;
757 const gboolean destination_in_slice
759 if(!target_in_slice && !destination_in_slice)
continue;
764 if(input_rgb[0] >= 1.f - 1e-6f && input_rgb[1] >= 1.f - 1e-6f && input_rgb[2] >= 1.f - 1e-6f)
787 for(
int sample_b = 0; sample_b < samples; sample_b++)
788 for(
int sample_g = 0; sample_g < samples; sample_g++)
789 for(
int sample_r = 0; sample_r < samples; sample_r++)
794 const dt_aligned_pixel_simd_t input_rgb = {
804 const size_t index = (size_t)(
r +
g * viewer->
clut_level + b * level2) * 3;
805 const dt_aligned_pixel_simd_t output_rgb = {
806 CLAMP(viewer->
clut[index + 0], 0.f, 1.f),
807 CLAMP(viewer->
clut[index + 1], 0.f, 1.f),
808 CLAMP(viewer->
clut[index + 2], 0.f, 1.f),
813 float x0 = 0.f, y0 = 0.f, depth0 = 0.f;
814 float x1 = 0.f, y1 = 0.f, depth1 = 0.f;
818 const gboolean target_in_slice
820 const gboolean destination_in_slice
822 if(!target_in_slice && !destination_in_slice)
continue;
839 "lut viewer swatch inputs");
841 "lut viewer swatch outputs");
861 collect_done = total_start;
862 convert_done = total_start;
869 float x0 = 0.f, y0 = 0.f, depth0 = 0.f;
870 float x1 = 0.f, y1 = 0.f, depth1 = 0.f;
876 cairo_arc(cr, x0, y0, target_radius, 0.f, 2.f *
M_PI_F);
879 cairo_fill_preserve(cr);
880 cairo_set_source_rgba(cr, 0.1, 0.1, 0.1, 0.6);
883 cairo_arc(cr, x1, y1, destination_radius, 0.f, 2.f *
M_PI_F);
886 cairo_fill_preserve(cr);
887 cairo_set_source_rgba(cr, 1.0, 1.0, 1.0, 0.5);
894 float x0 = 0.f, y0 = 0.f, depth0 = 0.f;
895 float x1 = 0.f, y1 = 0.f, depth1 = 0.f;
901 cairo_arc(cr, x0, y0, target_radius, 0.f, 2.f *
M_PI_F);
904 cairo_fill_preserve(cr);
905 cairo_set_source_rgba(cr, 0.2, 0.2, 0.2, 0.8);
908 cairo_arc(cr, x1, y1, destination_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);
920 "[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",
921 show_control_nodes ?
"controls" :
"lut", viewer->
clut_level, samples, max_samples, viewer->
sample_count, gamut,
922 rebuild_sample_cache ?
"rebuild" :
"reuse",
923 1000.0 * (collect_done - total_start),
924 1000.0 * (convert_done - collect_done),
925 1000.0 * (total_done - convert_done),
926 1000.0 * (total_done - total_start));
932 cairo_text_extents_t extents;
933 cairo_select_font_face(cr,
"sans", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL);
935 cairo_text_extents(cr, message, &extents);
936 cairo_set_source_rgba(cr, 0.9, 0.9, 0.9, 0.7);
937 cairo_move_to(cr, 0.5f * ((
float)
width - extents.width), 0.5f * ((
float)
height - extents.height));
938 cairo_show_text(cr, message);
949 MAX((
int)ceil((
double)
width * ppd), 1),
950 MAX((
int)ceil((
double)
height * ppd), 1));
951 cairo_surface_set_device_scale(viewer->
surface, ppd, ppd);
952 cairo_t *cr = cairo_create(viewer->
surface);
953 GtkStyleContext *context = gtk_widget_get_style_context(GTK_WIDGET(viewer->
area));
955 gtk_render_background(context, cr, 0, 0,
width,
height);
956 gtk_render_frame(context, cr, 0, 0,
width,
height);
965 "[lut_viewer] render_surface %dx%d ppd=%.2f placeholder=1 total=%.3fms\n",
987 "[lut_viewer] render_surface %dx%d ppd=%.2f placeholder=1 total=%.3fms\n",
1013 "[lut_viewer] render_surface %dx%d ppd=%.2f level=%u gamut=%d total=%.3fms\n",
1020 GtkAllocation allocation;
1021 gtk_widget_get_allocation(widget, &allocation);
1028 const float zoom = viewer->
zoom;
1078 cairo_set_source_surface(cr, viewer->
surface, 0, 0);
1089 gtk_widget_queue_draw(GTK_WIDGET(viewer->
area));
1095 GtkAllocation allocation;
1096 gtk_widget_get_allocation(widget, &allocation);
1097 float zoom_step = 1.f;
1099 switch(event->direction)
1104 case GDK_SCROLL_DOWN:
1105 zoom_step = 1.f / 1.1f;
1107 case GDK_SCROLL_SMOOTH:
1108 zoom_step = exp2f((
float)(-event->delta_y) * 0.2f);
1114 const float old_zoom = viewer->
zoom;
1115 const float new_zoom = CLAMP(old_zoom * zoom_step, 0.25f, 8.f);
1116 const float zoom_ratio = new_zoom / old_zoom;
1117 const float center_x = 0.5f * allocation.width;
1118 const float center_y = 0.5f * allocation.height;
1120 viewer->
pan_x =
event->x - center_x - zoom_ratio * (
event->x - center_x - viewer->
pan_x);
1121 viewer->
pan_y =
event->y - center_y - zoom_ratio * (
event->y - center_y - viewer->
pan_y);
1122 viewer->
zoom = new_zoom;
1124 gtk_widget_queue_draw(widget);
1131 if(event->type == GDK_2BUTTON_PRESS)
1138 viewer->
pan_x = 0.f;
1139 viewer->
pan_y = 0.f;
1142 gtk_widget_queue_draw(widget);
1146 if(event->button == 1)
1148 else if(event->button == 2)
1172 gtk_widget_queue_draw(widget);
1179 - 0.25f * (
float)(event->y - viewer->
drag_anchor_y), 0.f, 90.f);
1206 GtkFileChooserNative *filechooser = gtk_file_chooser_native_new(
1207 _(
"save 3D LUT"), GTK_WINDOW(win), GTK_FILE_CHOOSER_ACTION_SAVE,
1208 _(
"_save"), _(
"_cancel"));
1209 gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(filechooser),
FALSE);
1210 gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(filechooser),
TRUE);
1211 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(filechooser),
"lut-viewer-export.cube");
1215 gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(filechooser), lutfolder);
1219 GtkFileFilter *filter = GTK_FILE_FILTER(gtk_file_filter_new());
1220 gtk_file_filter_add_pattern(filter,
"*.cube");
1221 gtk_file_filter_add_pattern(filter,
"*.CUBE");
1222 gtk_file_filter_set_name(filter, _(
"3D lut (cube)"));
1223 gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(filechooser), filter);
1224 gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(filechooser), filter);
1226 if(gtk_native_dialog_run(GTK_NATIVE_DIALOG(filechooser)) == GTK_RESPONSE_ACCEPT)
1228 gchar *filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(filechooser));
1229 char *path = filename;
1230 FILE *cube_file = NULL;
1232 if(!g_str_has_suffix(filename,
".cube") && !g_str_has_suffix(filename,
".CUBE"))
1233 path = g_strconcat(filename,
".cube", NULL);
1235 cube_file = g_fopen(path,
"wb");
1242 const char *
const lut_profile_name
1245 fprintf(cube_file,
"# generated by ansel LUT viewer\n");
1246 fprintf(cube_file,
"# LUT_COLORSPACE %s\n", lut_profile_name);
1247 fprintf(cube_file,
"TITLE \"lut viewer export\"\n");
1248 fprintf(cube_file,
"LUT_3D_SIZE %u\n", viewer->
clut_level);
1249 fprintf(cube_file,
"DOMAIN_MIN 0.0 0.0 0.0\n");
1250 fprintf(cube_file,
"DOMAIN_MAX 1.0 1.0 1.0\n");
1252 for(uint16_t b = 0; b < viewer->
clut_level; b++)
1258 fprintf(cube_file,
"%.7f %.7f %.7f\n",
1259 viewer->
clut[index + 0], viewer->
clut[index + 1], viewer->
clut[index + 2]);
1267 if(path != filename) g_free(path);
1272 g_object_unref(filechooser);
1284 gtk_widget_add_events(GTK_WIDGET(viewer->
area),
1285 GDK_SCROLL_MASK | GDK_SMOOTH_SCROLL_MASK | GDK_BUTTON_PRESS_MASK
1286 | GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK);
1287 g_signal_connect(G_OBJECT(viewer->
area),
"draw", G_CALLBACK(
_draw_callback), viewer);
1288 g_signal_connect(G_OBJECT(viewer->
area),
"scroll-event", G_CALLBACK(
_scroll_callback), viewer);
1292 gtk_box_pack_start(GTK_BOX(viewer->
widget), GTK_WIDGET(viewer->
area),
TRUE,
TRUE, 0);
1329 viewer->
show_control_nodes = gtk_check_button_new_with_label(_(
"show control nodes"));
1344 viewer->
save_button = gtk_button_new_with_label(_(
"save to cLUT"));
1348 GtkWidget *expander = gtk_expander_new(_(
"viewer controls"));
1349 gtk_expander_set_expanded(GTK_EXPANDER(expander),
FALSE);
1350 gtk_container_add(GTK_CONTAINER(expander), viewer->
controls);
1354 viewer->
pan_x = 0.f;
1355 viewer->
pan_y = 0.f;
1394 return viewer ? viewer->
widget : NULL;
1404 viewer->
clut = clut;
1415 size_t control_node_count)
1424 const gboolean enabled = control_nodes && control_node_count > 0;
1435 if(viewer) gtk_widget_queue_draw(GTK_WIDGET(viewer->
area));
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)
static void set_color(cairo_t *cr, GdkRGBA color)
const dt_colorspaces_color_profile_t * dt_colorspaces_get_profile(dt_colorspaces_color_profile_type_t type, const char *filename, dt_colorspaces_profile_direction_t direction)
const char * dt_colorspaces_get_name(dt_colorspaces_color_profile_type_t type, const char *filename)
dt_colorspaces_color_profile_type_t
@ DT_COLORSPACE_PQ_REC2020
@ DT_COLORSPACE_HLG_REC2020
@ DT_COLORSPACE_LIN_REC2020
@ DT_COLORSPACE_DISPLAY_P3
@ DT_COLORSPACE_LIN_REC709
@ DT_PROFILE_DIRECTION_ANY
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)
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,...)
void dt_print(dt_debug_thread_t thread, const char *msg,...)
#define dt_free_align(ptr)
static void * dt_calloc_align(size_t size)
#define __OMP_SIMD__(...)
static const dt_aligned_pixel_simd_t value
static double dt_get_wtime(void)
#define IS_NULL_PTR(p)
C is way too permissive with !=, == and if(var) checks, which can mean too many things depending on w...
GtkWidget * dtgtk_drawing_area_new_with_aspect_ratio(double aspect)
#define dt_pthread_rwlock_t
#define dt_pthread_rwlock_unlock
#define dt_pthread_rwlock_rdlock
static cairo_surface_t * dt_cairo_image_surface_create(cairo_format_t format, int width, int height)
GtkWidget * dt_ui_main_window(dt_ui_t *ui)
get the main window widget
#define DT_GUI_BOX_SPACING
#define DT_PIXEL_APPLY_DPI(value)
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
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)
float DT_ALIGNED_ARRAY dt_colormatrix_t[4][4]
static int mat3SSEinv(dt_colormatrix_t dst, const dt_colormatrix_t src)
float dt_aligned_pixel_t[4]
struct _GtkWidget GtkWidget
struct dt_gui_gtk_t * gui
struct dt_bauhaus_t * bauhaus
The dt_gui_module_t type is the intersection between a dt_lib_module_t and a dt_iop_module_t structur...
dt_colormatrix_t matrix_out_transposed
dt_colorspaces_color_profile_type_t type
dt_colormatrix_t matrix_out
char filename[DT_IOP_COLOR_ICC_LEN]
dt_colormatrix_t matrix_in_transposed
dt_colormatrix_t matrix_in
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