73#define DT_GUI_CURVE_EDITOR_INSET DT_PIXEL_APPLY_DPI(1)
74#define DT_IOP_RGBCURVE_RES 256
75#define DT_IOP_RGBCURVE_MAXNODES 20
76#define DT_IOP_RGBCURVE_MIN_X_DISTANCE 0.0025f
79#define DT_IOP_COLOR_ICC_LEN 512
175 _(
"corrective and creative"),
176 _(
"linear, RGB, display-referred"),
177 _(
"non-linear, RGB"),
178 _(
"linear, RGB, display-referred"));
184 memset(&
p, 0,
sizeof(
p));
192 p.compensate_middle_grey = 1;
193 p.preserve_colors = 1;
195 float linear_ab[7] = { 0.0, 0.08, 0.3, 0.5, 0.7, 0.92, 1.0 };
217 self->version(), &
p,
sizeof(
p), 1);
220 float linear_L[7] = { 0.0, 0.08, 0.17, 0.50, 0.83, 0.92, 1.0 };
226 self->version(), &
p,
sizeof(
p), 1);
236 self->version(), &
p,
sizeof(
p), 1);
245 self->version(), &
p,
sizeof(
p), 1);
254 for(
int k = 1;
k < 6;
k++)
256 for(
int k = 1;
k < 6;
k++)
259 self->version(), &
p,
sizeof(
p), 1);
267 for(
int k = 1;
k < 6;
k++)
269 for(
int k = 1;
k < 6;
k++)
272 self->version(), &
p,
sizeof(
p), 1);
284 self->version(), &
p,
sizeof(
p), 1);
289 self->version(), &
p,
sizeof(
p), 1);
294 self->version(), &
p,
sizeof(
p), 1);
299 self->version(), &
p,
sizeof(
p), 1);
304 return (
x - offset) * zoom_factor;
309 return (
x / zoom_factor) + offset;
315 switch(
p->curve_autoscale)
318 if(
p->compensate_middle_grey && !
IS_NULL_PTR(work_profile))
320 for(
int c = 0; c < 3; c++)
out[c] = dt_ioppr_compensate_middle_grey(in[c], work_profile);
324 for(
int c = 0; c < 3; c++)
out[c] = in[c];
330 = (work_profile) ? dt_ioppr_get_rgb_matrix_luminance(in,
333 work_profile->unbounded_coeffs_in,
337 if(
p->compensate_middle_grey && !
IS_NULL_PTR(work_profile))
339 out[0] = dt_ioppr_compensate_middle_grey(val, work_profile);
350 for(
int c = 0; c < 3; c++)
out[c] = CLAMP(
out[c], 0.0f, 1.0f);
362 for(
int k=0;
k<
p->curve_num_nodes[channel];
k++)
363 if(
p->curve_nodes[channel][
k].x !=
p->curve_nodes[channel][
k].y)
return FALSE;
373 if(w ==
g->autoscale)
395 else if(w ==
g->chk_compensate_middle_grey)
403 for(
int k = 0;
k <
p->curve_num_nodes[
ch];
k++)
405 if(
p->compensate_middle_grey)
408 p->curve_nodes[
ch][
k].x = dt_ioppr_compensate_middle_grey(
p->curve_nodes[
ch][
k].x, work_profile);
409 p->curve_nodes[
ch][
k].y = dt_ioppr_compensate_middle_grey(
p->curve_nodes[
ch][
k].y, work_profile);
414 p->curve_nodes[
ch][
k].x = dt_ioppr_uncompensate_middle_grey(
p->curve_nodes[
ch][
k].x, work_profile);
415 p->curve_nodes[
ch][
k].y = dt_ioppr_uncompensate_middle_grey(
p->curve_nodes[
ch][
k].y, work_profile);
443 gtk_widget_queue_draw(GTK_WIDGET(
g->area));
454 gtk_widget_queue_draw(self->
gui->
widget);
460 GtkAllocation allocation;
461 gtk_widget_get_allocation(widget, &allocation);
462 r.width = allocation.width;
463 r.height = allocation.width;
464 gtk_widget_get_preferred_size(widget, &
r, NULL);
471 if(curve_nodes[0].
x >
x)
475 for(
int k = 1;
k < *nodes;
k++)
477 if(curve_nodes[
k].
x >
x)
487 curve_nodes[
i].
x = curve_nodes[
i - 1].
x;
488 curve_nodes[
i].
y = curve_nodes[
i - 1].
y;
505 val = (work_profile) ? dt_ioppr_get_rgb_matrix_luminance(in,
508 work_profile->unbounded_coeffs_in,
515 if(
p->compensate_middle_grey && !
IS_NULL_PTR(work_profile))
516 y =
x = dt_ioppr_compensate_middle_grey(val, work_profile);
533 if(picker ==
g->colorpicker_set_values)
538 const int ch =
g->channel;
542 p->curve_num_nodes[
ch] =
d->curve_num_nodes[
ch];
543 p->curve_type[
ch] =
d->curve_type[
ch];
546 p->curve_nodes[
ch][
k].x =
d->curve_nodes[
ch][
k].x;
547 p->curve_nodes[
ch][
k].y =
d->curve_nodes[
ch][
k].y;
551 int picker_set_upper_lower;
553 picker_set_upper_lower = 1;
555 picker_set_upper_lower = -1;
557 picker_set_upper_lower = 0;
560 const float increment = 0.05f * picker_set_upper_lower;
566 if(
p->curve_num_nodes[
ch] == 5)
568 p->curve_nodes[
ch][1].x - increment + (
p->curve_nodes[
ch][3].x -
p->curve_nodes[
ch][1].x) / 2.f,
569 p->curve_nodes[
ch][1].y + increment + (
p->curve_nodes[
ch][3].y -
p->curve_nodes[
ch][1].y) / 2.f);
580 gboolean point_valid =
TRUE;
603 const int ch =
g->channel;
606 const float new_x = CLAMP(curve[
g->selected].
x + dx, 0.0f, 1.0f);
607 const float new_y = CLAMP(curve[
g->selected].
y + dy, 0.0f, 1.0f);
609 gtk_widget_queue_draw(widget);
613 curve[
g->selected].
x = new_x;
614 curve[
g->selected].
y = new_y;
622#define RGBCURVE_DEFAULT_STEP (0.001f)
634 if(
g->selected < 0)
return TRUE;
655 if(
g->selected < 0)
return FALSE;
659 float dx = 0.0f, dy = 0.0f;
660 if(
key == GDK_KEY_Up)
665 else if(
key == GDK_KEY_Down)
670 else if(
key == GDK_KEY_Right)
675 else if(
key == GDK_KEY_Left)
681 if(!handled)
return FALSE;
687#undef RGBCURVE_DEFAULT_STEP
691 gtk_widget_queue_draw(widget);
697 gtk_widget_queue_draw(widget);
707 const int ch =
g->channel;
708 const int nodes =
p->curve_num_nodes[
ch];
709 const int autoscale =
p->curve_autoscale;
712 if(
g->minmax_curve_type[
ch] !=
p->curve_type[
ch] ||
g->minmax_curve_nodes[
ch] !=
p->curve_num_nodes[
ch])
716 g->minmax_curve_nodes[
ch] =
p->curve_num_nodes[
ch];
717 g->minmax_curve_type[
ch] =
p->curve_type[
ch];
718 for(
int k = 0;
k <
p->curve_num_nodes[
ch];
k++)
723 for(
int k = 0;
k <
p->curve_num_nodes[
ch];
k++)
730 const float xm = curve_nodes[nodes - 1].
x;
732 const float x[4] = { 0.7f * xm, 0.8f * xm, 0.9f * xm, 1.0f * xm };
741 GtkAllocation allocation;
742 gtk_widget_get_allocation(widget, &allocation);
743 int width = allocation.width,
height = allocation.height;
745 cairo_t *cr = cairo_create(cst);
748 cairo_set_source_rgb(cr, .2, .2, .2);
751 cairo_translate(cr, inset, inset);
757 cairo_set_source_rgb(cr, .1, .1, .1);
761 cairo_set_source_rgb(cr, .3, .3, .3);
767 cairo_set_source_rgb(cr, .1, .1, .1);
769 cairo_translate(cr, 0,
height);
771 dt_draw_grid_zoomed(cr, 4, 0.f, 0.f, 1.f, 1.f,
width,
height,
g->zoom_factor,
g->offset_x,
g->offset_y);
773 const double dashed[] = { 4.0, 4.0 };
774 const int len =
sizeof(dashed) /
sizeof(dashed[0]);
775 cairo_set_dash(cr, dashed, len, 0);
776 dt_draw_grid_zoomed(cr, 8, 0.f, 0.f, 1.f, 1.f,
width,
height,
g->zoom_factor,
g->offset_x,
g->offset_y);
777 cairo_set_dash(cr, dashed, 0, 0);
791 cairo_set_source_rgb(cr, 0.6, 0.6, 0.6);
793 for(
int k = 0;
k < nodes;
k++)
807 cairo_set_source_rgb(cr, .9, .9, .9);
820 const gboolean is_linear =
FALSE;
829 hist_max = logf(1.0 + hist_max);
833 cairo_push_group_with_content(cr, CAIRO_CONTENT_COLOR);
838 cairo_set_operator(cr, CAIRO_OPERATOR_ADD);
853 cairo_pop_group_to_source(cr);
854 cairo_paint_with_alpha(cr, 0.2);
871 for(; samples; samples = g_slist_next(samples))
876 for(
int k = 0;
k < 3;
k++)
882 picker_mean[3] = picker_min[3] = picker_max[3] = 1.f;
885 work_profile,
"rgb curve");
900 cairo_set_source_rgba(cr, 0.5, 0.7, 0.5, 0.15);
901 cairo_rectangle(cr,
width * picker_min[
ch], 0,
width * fmax(picker_max[
ch] - picker_min[
ch], 0.0f),
904 cairo_set_source_rgba(cr, 0.5, 0.7, 0.5, 0.5);
905 cairo_move_to(cr,
width * picker_mean[
ch], 0);
917 PangoFontDescription *desc = pango_font_description_copy_static(
dt_bauhaus_get_global()->pango_font_desc);
918 pango_font_description_set_weight(desc, PANGO_WEIGHT_BOLD);
919 pango_font_description_set_absolute_size(desc, PANGO_SCALE);
920 layout = pango_cairo_create_layout(cr);
921 pango_layout_set_font_description(layout, desc);
928 snprintf(text,
sizeof(text),
"100.00 / 100.00 ( +100.00)");
929 pango_layout_set_text(layout, text, -1);
930 pango_layout_get_pixel_extents(layout, &ink, NULL);
931 pango_font_description_set_absolute_size(desc,
width * 1.0 / ink.width * PANGO_SCALE);
932 pango_layout_set_font_description(layout, desc);
938 cairo_set_source_rgba(cr, 0.7, 0.5, 0.5, 0.33);
939 cairo_rectangle(cr,
width * picker_min[
ch], 0,
width * fmax(picker_max[
ch] - picker_min[
ch], 0.0f),
942 cairo_set_source_rgba(cr, 0.9, 0.7, 0.7, 0.5);
943 cairo_move_to(cr,
width * picker_mean[
ch], 0);
949 snprintf(text,
sizeof(text),
"%.1f \342\206\222 %.1f", picker_mean[
ch] * 255.f, picker_min[
ch] * 255.f);
951 cairo_set_source_rgb(cr, 0.1, 0.1, 0.1);
953 pango_layout_set_text(layout, text, -1);
954 pango_layout_get_pixel_extents(layout, &ink, NULL);
955 cairo_move_to(cr, 0.02f *
width, -0.94 *
height - ink.height - ink.y);
956 pango_cairo_show_layout(cr, layout);
958 pango_font_description_free(desc);
959 g_object_unref(layout);
970 PangoFontDescription *desc = pango_font_description_copy_static(
dt_bauhaus_get_global()->pango_font_desc);
971 pango_font_description_set_weight(desc, PANGO_WEIGHT_BOLD);
972 pango_font_description_set_absolute_size(desc, PANGO_SCALE);
973 layout = pango_cairo_create_layout(cr);
974 pango_layout_set_font_description(layout, desc);
977 snprintf(text,
sizeof(text),
"100.00 / 100.00 ( +100.00)");
978 pango_layout_set_text(layout, text, -1);
979 pango_layout_get_pixel_extents(layout, &ink, NULL);
980 pango_font_description_set_absolute_size(desc,
width * 1.0 / ink.width * PANGO_SCALE);
981 pango_layout_set_font_description(layout, desc);
983 const float min_scale_value = 0.0f;
984 const float max_scale_value = 255.0f;
986 const float x_node_value = curve_nodes[
g->selected].
x * (max_scale_value - min_scale_value) + min_scale_value;
987 const float y_node_value = curve_nodes[
g->selected].
y * (max_scale_value - min_scale_value) + min_scale_value;
988 const float d_node_value = y_node_value - x_node_value;
989 snprintf(text,
sizeof(text),
"%.1f / %.1f ( %+.1f)", x_node_value, y_node_value, d_node_value);
991 cairo_set_source_rgb(cr, 0.1, 0.1, 0.1);
992 pango_layout_set_text(layout, text, -1);
993 pango_layout_get_pixel_extents(layout, &ink, NULL);
994 cairo_move_to(cr, 0.98f *
width - ink.width - ink.x, -0.02 *
height - ink.height - ink.y);
995 pango_cairo_show_layout(cr, layout);
997 pango_font_description_free(desc);
998 g_object_unref(layout);
1001 cairo_set_source_rgb(cr, .9, .9, .9);
1011 cairo_set_source_rgb(cr, .9, .9, .9);
1014 cairo_move_to(cr, 0, -
height * y_offset);
1039 cairo_set_source_surface(crf, cst, 0, 0);
1041 cairo_surface_destroy(cst);
1052 const int ch =
g->channel;
1053 const int nodes =
p->curve_num_nodes[
ch];
1059 GtkAllocation allocation;
1060 gtk_widget_get_allocation(widget, &allocation);
1061 const int height = allocation.height - 2 * inset,
width = allocation.width - 2 * inset;
1063 const double old_m_x =
g->mouse_x;
1064 const double old_m_y =
g->mouse_y;
1066 g->mouse_x = CLAMP(event->x - inset, 0,
width) / (float)
width;
1067 g->mouse_y = 1.0 - CLAMP(event->y - inset, 0,
height) / (float)
height;
1069 const float mx =
g->mouse_x;
1070 const float my =
g->mouse_y;
1074 if(event->state & GDK_BUTTON1_MASK)
1077 if(
g->selected >= 0)
1080 const float translate_mouse_x
1082 const float translate_mouse_y
1085 const float dx =
_mouse_to_curve(
g->mouse_x - translate_mouse_x,
g->zoom_factor,
g->offset_x)
1087 const float dy =
_mouse_to_curve(
g->mouse_y - translate_mouse_y,
g->zoom_factor,
g->offset_y)
1093 else if(nodes < DT_IOP_RGBCURVE_MAXNODES && g->
selected >= -1)
1097 g->selected =
_add_node(curve_nodes, &
p->curve_num_nodes[
ch], linx, liny);
1104 float min = .04f * .04f;
1106 for(
int k = 0;
k < nodes;
k++)
1118 g->selected = nearest;
1121 if(
g->selected >= 0) gtk_widget_grab_focus(widget);
1122 gtk_widget_queue_draw(widget);
1132 const int ch =
g->channel;
1133 const int autoscale =
p->curve_autoscale;
1134 const int nodes =
p->curve_num_nodes[
ch];
1137 if(event->button == 1)
1144 GtkAllocation allocation;
1145 gtk_widget_get_allocation(widget, &allocation);
1146 const int width = allocation.width - 2 * inset;
1147 const int height = allocation.height - 2 * inset;
1149 g->mouse_x = CLAMP(event->x - inset, 0,
width) / (float)
width;
1150 g->mouse_y = 1.0 - CLAMP(event->y - inset, 0,
height) / (float)
height;
1152 const float mx =
g->mouse_x;
1157 if(curve_nodes[0].
x > mx)
1161 for(
int k = 1;
k < nodes;
k++)
1163 if(curve_nodes[
k].
x > mx)
1175 if(y >= 0.0f && y <= 1.0f)
1181 const float min = .04f * .04f;
1182 for(
int k = 0;
k < nodes;
k++)
1184 const float other_y =
_curve_to_mouse(curve_nodes[
k].y,
g->zoom_factor,
g->offset_y);
1185 const float dist = (y - other_y) * (y - other_y);
1191 gtk_widget_queue_draw(self->
gui->
widget);
1196 else if(event->type == GDK_2BUTTON_PRESS)
1202 p->curve_num_nodes[
ch] =
d->curve_num_nodes[
ch];
1203 p->curve_type[
ch] =
d->curve_type[
ch];
1204 for(
int k = 0;
k <
d->curve_num_nodes[
ch];
k++)
1206 p->curve_nodes[
ch][
k].x =
d->curve_nodes[
ch][
k].x;
1207 p->curve_nodes[
ch][
k].y =
d->curve_nodes[
ch][
k].y;
1213 gtk_widget_queue_draw(self->
gui->
widget);
1224 gtk_widget_queue_draw(self->
gui->
widget);
1230 else if(event->button == 3 &&
g->selected >= 0)
1232 if(
g->selected == 0 ||
g->selected == nodes - 1)
1234 const float reset_value =
g->selected == 0 ? 0.f : 1.f;
1235 curve_nodes[
g->selected].
y = curve_nodes[
g->selected].
x = reset_value;
1238 gtk_widget_queue_draw(self->
gui->
widget);
1242 for(
int k =
g->selected;
k < nodes - 1;
k++)
1244 curve_nodes[
k].
x = curve_nodes[
k + 1].
x;
1245 curve_nodes[
k].
y = curve_nodes[
k + 1].
y;
1247 curve_nodes[nodes - 1].
x = curve_nodes[nodes - 1].
y = 0;
1249 p->curve_num_nodes[
ch]--;
1252 gtk_widget_queue_draw(self->
gui->
widget);
1265 g->offset_x =
g->offset_y = 0.f;
1266 g->zoom_factor = 1.f;
1270 gtk_widget_queue_draw(self->
gui->
widget);
1280 g->mouse_x =
g->mouse_y = -1.0;
1282 g->offset_x =
g->offset_y = 0.f;
1283 g->zoom_factor = 1.f;
1295 g->minmax_curve_nodes[
ch] =
p->curve_num_nodes[
ch];
1296 g->minmax_curve_type[
ch] =
p->curve_type[
ch];
1297 for(
int k = 0;
k <
p->curve_num_nodes[
ch];
k++)
1306 gtk_widget_set_tooltip_text(
g->autoscale, _(
"choose between linked and independent channels."));
1310 g->channel_tabs = GTK_NOTEBOOK(gtk_notebook_new());
1314 g_signal_connect(G_OBJECT(
g->channel_tabs),
"switch_page", G_CALLBACK(
tab_switch_callback), self);
1316 gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(
g->channel_tabs),
TRUE,
TRUE, 0);
1317 gtk_box_pack_start(GTK_BOX(hbox), gtk_grid_new(),
TRUE,
TRUE, 0);
1321 gtk_widget_set_tooltip_text(
g->colorpicker, _(
"pick GUI color from image\nctrl+click or right-click to select an area"));
1327 gtk_widget_set_tooltip_text(
g->colorpicker_set_values, _(
"create a curve based on an area from the image\n"
1328 "drag to create a flat curve\n"
1329 "ctrl+drag to create a positive curve\n"
1330 "shift+drag to create a negative curve"));
1334 gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(hbox),
TRUE,
TRUE, 0);
1336 g->area = GTK_DRAWING_AREA(gtk_drawing_area_new());
1337 gtk_widget_set_hexpand(GTK_WIDGET(
g->area),
TRUE);
1338 g_object_set_data(G_OBJECT(
g->area),
"iop-instance", self);
1339 gtk_box_pack_start(GTK_BOX(vbox),
1341 "plugins/darkroom/rgbcurve/graphheight", 280, 100),
1348 | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK
1349 | GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK);
1350 gtk_widget_set_can_focus(GTK_WIDGET(
g->area),
TRUE);
1371 gtk_widget_set_tooltip_text(
g->interpolator,
1372 _(
"change this method if you see oscillations or cusps in the curve\n"
1373 "- cubic spline is better to produce smooth curves but oscillates when nodes are too close\n"
1374 "- centripetal is better to avoids cusps and oscillations with close nodes but is less smooth\n"
1375 "- monotonic is better for accuracy of pure analytical functions (log, gamma, exp)\n"));
1379 gtk_widget_set_tooltip_text(
g->chk_compensate_middle_grey, _(
"compensate middle gray"));
1382 gtk_widget_set_tooltip_text(
g->cmb_preserve_colors, _(
"method to preserve colors when applying contrast"));
1392 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(
g->chk_compensate_middle_grey),
p->compensate_middle_grey);
1399 gtk_widget_queue_draw(self->
gui->
widget);
1417 piece->
data = (
void *)
d;
1457 module->request_histogram |= (DT_REQUEST_ON);
1462 d->curve_nodes[1][1].x =
d->curve_nodes[1][1].y =
1463 d->curve_nodes[2][1].x =
d->curve_nodes[2][1].y = 1.0;
1465 module->histogram_middle_grey = d->compensate_middle_grey;
1479 if(
d->type_work == work_profile->
type && strcmp(
d->filename_work, work_profile->
filename) == 0)
return;
1482 if(!
IS_NULL_PTR(work_profile) &&
d->params.compensate_middle_grey)
1484 d->type_work = work_profile->
type;
1485 g_strlcpy(
d->filename_work, work_profile->
filename,
sizeof(
d->filename_work));
1489 for(
int k = 0;
k <
d->params.curve_num_nodes[
ch];
k++)
1491 curve_nodes[
ch][
k].
x = dt_ioppr_uncompensate_middle_grey(
d->params.curve_nodes[
ch][
k].x, work_profile);
1492 curve_nodes[
ch][
k].
y = dt_ioppr_uncompensate_middle_grey(
d->params.curve_nodes[
ch][
k].y, work_profile);
1507 if(
d->curve_changed[
ch])
1511 for(
int k = 0;
k <
d->params.curve_num_nodes[
ch];
k++)
1516 for(
int k = 0;
k <
d->params.curve_num_nodes[
ch];
k++)
1526 const float xm_L = curve_nodes[
ch][
d->params.curve_num_nodes[
ch] - 1].
x;
1527 const float x_L[4] = { 0.7f * xm_L, 0.8f * xm_L, 0.9f * xm_L, 1.0f * xm_L };
1528 const float y_L[4] = {
d->table[
ch][CLAMP((
int)(x_L[0] * 0x10000ul), 0, 0xffff)],
1529 d->table[
ch][CLAMP((
int)(x_L[1] * 0x10000ul), 0, 0xffff)],
1530 d->table[
ch][CLAMP((
int)(x_L[2] * 0x10000ul), 0, 0xffff)],
1531 d->table[
ch][CLAMP((
int)(x_L[3] * 0x10000ul), 0, 0xffff)] };
1548 d->curve_changed[
ch]
1549 = (
d->params.curve_type[
ch] !=
p->curve_type[
ch] ||
d->params.curve_nodes[
ch] !=
p->curve_nodes[
ch]);
1555 d->filename_work[0] =
'\0';
1566 const float *
const restrict in = (
float*)ivoid;
1567 float *
const restrict
out = (
float*)
ovoid;
1580 const int autoscale =
d->params.curve_autoscale;
1584 for(
int y = 0; y < 4*npixels; y += 4)
1588 out[y+0] = (in[y+0] < xm_L) ? table[
DT_IOP_RGBCURVE_R][CLAMP((
int)(in[y+0] * 0x10000ul), 0, 0xffff)]
1590 out[y+1] = (in[y+1] < xm_g) ? table[
DT_IOP_RGBCURVE_G][CLAMP((
int)(in[y+1] * 0x10000ul), 0, 0xffff)]
1592 out[y+2] = (in[y+2] < xm_b) ? table[
DT_IOP_RGBCURVE_B][CLAMP((
int)(in[y+2] * 0x10000ul), 0, 0xffff)]
1599 for(
int c = 0; c < 3; c++)
1601 out[y+c] = (in[y+c] < xm_L) ? table[
DT_IOP_RGBCURVE_R][CLAMP((
int)(in[y+c] * 0x10000ul), 0, 0xffff)]
1608 const float lum =
dt_rgb_norm(in + y,
d->params.preserve_colors, work_profile);
1611 const float curve_lum = (lum < xm_L)
1614 ratio = curve_lum / lum;
1616 for(
size_t c = 0; c < 3; c++)
1618 out[y+c] = (ratio * in[y+c]);
1628#undef DT_GUI_CURVE_EDITOR_INSET
1629#undef DT_IOP_RGBCURVE_RES
1630#undef DT_IOP_RGBCURVE_MAXNODES
1631#undef DT_IOP_RGBCURVE_MIN_X_DISTANCE
1632#undef DT_IOP_COLOR_ICC_LEN
Handle default and user-set shortcuts (accelerators)
static double dist(double x1, double y1, double x2, double y2)
int dt_bauhaus_combobox_get(GtkWidget *widget)
void dt_bauhaus_combobox_set(GtkWidget *widget, const int pos)
void dt_bauhaus_widget_set_label(GtkWidget *widget, const char *label)
GtkWidget * dt_bauhaus_combobox_new(dt_bauhaus_t *bh, dt_gui_module_t *self)
void dt_bauhaus_combobox_add(GtkWidget *widget, const char *text)
typedef void((*dt_cache_allocate_t)(void *userdata, dt_cache_entry_t *entry))
static float dt_camera_rgb_luminance(const float4 rgb)
void dt_iop_color_picker_reset(dt_iop_module_t *module, gboolean keep)
GtkWidget * dt_color_picker_new(dt_iop_module_t *module, dt_iop_color_picker_kind_t kind, GtkWidget *w)
@ DT_COLOR_PICKER_POINT_AREA
@ DT_LIB_COLORPICKER_STATISTIC_MAX
@ DT_LIB_COLORPICKER_STATISTIC_MIN
@ DT_LIB_COLORPICKER_STATISTIC_MEAN
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 dt_aligned_pixel_t float *const const float unbounded_coeffs[3][3]
struct _GtkWidget GtkWidget
GtkWidget, opaque, spelled exactly as GTK spells it.
static const float const float const float min
const dt_colormatrix_t dt_aligned_pixel_t out
void dt_control_queue_redraw_widget(GtkWidget *widget)
threadsafe request of redraw of specific widget. Use this function if you need to redraw a specific w...
struct dt_bauhaus_t * dt_bauhaus_get_global(void)
static float dt_rgb_norm(const float4 in, const int norm, const int work_profile, constant dt_colorspaces_iccprofile_info_cl_t *profile_info, read_only image2d_t lut)
#define dt_dev_add_history_item(dev, module, enable, redraw)
dt_iop_order_iccprofile_info_t * dt_ioppr_get_pipe_output_profile_info(const struct dt_dev_pixelpipe_t *pipe)
dt_iop_order_iccprofile_info_t * dt_ioppr_get_pipe_work_profile_info(const struct dt_dev_pixelpipe_t *pipe)
__DT_CLONE_TARGETS__ dt_iop_order_iccprofile_info_t * dt_ioppr_get_iop_work_profile_info(struct dt_iop_module_t *module, GList *iop_list)
gboolean dt_dev_pixelpipe_has_preview_output(const dt_develop_t *dev, const dt_dev_pixelpipe_t *pipe, const dt_iop_roi_t *roi)
GHashTable * selected
set of checked row labels, mirrored to conf on every change
static void dt_draw_curve_calc_values(dt_draw_curve_t *c, const float min, const float max, const int res, float *x, float *y)
static void dt_draw_histogram_8_zoomed(cairo_t *cr, const uint32_t *hist, int32_t channels, int32_t channel, const float zoom_factor, const float zoom_offset_x, const float zoom_offset_y, gboolean linear)
static void set_color(cairo_t *cr, GdkRGBA color)
static float dt_draw_curve_calc_value(dt_draw_curve_t *c, const float x)
static void dt_draw_curve_destroy(dt_draw_curve_t *c)
static void dt_draw_curve_set_point(dt_draw_curve_t *c, const int num, const float x, const float y)
static int dt_draw_curve_add_point(dt_draw_curve_t *c, const float x, const float y)
static dt_draw_curve_t * dt_draw_curve_new(const float min, const float max, unsigned int type)
static void dt_draw_grid_zoomed(cairo_t *cr, const int num, const float left, const float top, const float right, const float bottom, const float width, const float height, const float zoom_factor, const float zoom_offset_x, const float zoom_offset_y)
static guint dt_keys_mainpad_alternatives(const guint key_val)
Remap keypad keys to usual mainpad ones.
void dt_gui_presets_add_generic(const char *name, dt_dev_operation_t op, const int32_t version, const void *params, const int32_t params_size, const int32_t enabled)
void dt_iop_default_init(dt_iop_module_t *module)
const char ** dt_iop_set_description(dt_iop_module_t *module, const char *main_text, const char *purpose, const char *input, const char *process, const char *output)
@ DT_REQUEST_COLORPICK_OFF
@ IOP_FLAGS_SUPPORTS_BLENDING
GtkWidget * dt_bauhaus_toggle_from_params(dt_iop_module_t *self, const char *param)
GtkWidget * dt_bauhaus_combobox_from_params(dt_iop_module_t *self, const char *param)
static dt_iop_gui_data_t * dt_iop_gui_data(const struct dt_iop_module_t *m)
The module's GUI data blob, NULL-safe for headless callers: IOP process() implementations read it for...
#define IOP_GUI_ALLOC(module)
static void dt_iop_estimate_exp(const float *const x, const float *const y, const int num, float *coeff)
static float dt_iop_eval_exp(const float *const coeff, const float x)
float *const restrict const size_t k
float *const restrict const size_t const size_t ch
#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_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_MODULE_INTROSPECTION(MODVER, PARAMSTYPE)
DT_MODULE() for a module whose params struct is introspected.
GtkWidget * dt_ui_notebook_page(GtkNotebook *notebook, const char *text, const char *tooltip)
void dt_ui_notebook_set_picker_owner(GtkNotebook *notebook, gpointer owner)
Register an opaque owner for a GtkNotebook's page switches, and report every "switch_page" to the hos...
#define __OMP_PARALLEL_FOR__(...)
dt_colorspaces_color_profile_type_t
@ DT_COLORSPACE_NONE
No profile / "take it from the image settings". Never matches a list entry.
static gboolean _area_draw_callback(GtkWidget *widget, cairo_t *crf, dt_iop_module_t *self)
void commit_params(struct dt_iop_module_t *self, dt_iop_params_t *p1, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
void init(dt_iop_module_t *module)
const char ** description(struct dt_iop_module_t *self)
__DT_CLONE_TARGETS__ int process(struct dt_iop_module_t *self, const dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece, const void *const ivoid, void *const ovoid)
static void interpolator_callback(GtkWidget *widget, dt_iop_module_t *self)
static void _rgbcurve_show_hide_controls(dt_iop_rgbcurve_params_t *p, dt_iop_rgbcurve_gui_data_t *g)
static gboolean _area_key_press_callback(GtkWidget *widget, GdkEventKey *event, dt_iop_module_t *self)
#define DT_IOP_RGBCURVE_RES
static __DT_CLONE_TARGETS__ void _generate_curve_lut(const dt_dev_pixelpipe_t *pipe, dt_iop_rgbcurve_data_t *d)
#define DT_GUI_CURVE_EDITOR_INSET
static gboolean _area_leave_notify_callback(GtkWidget *widget, GdkEventCrossing *event, gpointer user_data)
#define DT_IOP_RGBCURVE_MIN_X_DISTANCE
static void picker_scale(const float *const in, float *out, dt_iop_rgbcurve_params_t *p, const dt_iop_order_iccprofile_info_t *const work_profile)
void init_pipe(struct dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
@ DT_IOP_RGBCURVE_MAX_CHANNELS
static float _mouse_to_curve(const float x, const float zoom_factor, const float offset)
static int _add_node(dt_iop_rgbcurve_node_t *curve_nodes, int *nodes, float x, float y)
#define DT_IOP_COLOR_ICC_LEN
void gui_reset(struct dt_iop_module_t *self)
static gboolean _sanity_check(const float x, const int selected, const int nodes, const dt_iop_rgbcurve_node_t *curve)
void gui_update(struct dt_iop_module_t *self)
Refresh GUI controls from current params and configuration.
void change_image(struct dt_iop_module_t *self)
float(* _coeffs_table_ptr)[3]
void gui_init(struct dt_iop_module_t *self)
dt_iop_rgbcurve_autoscale_t
@ DT_S_SCALE_AUTOMATIC_RGB
void gui_changed(dt_iop_module_t *self, GtkWidget *w, void *previous)
static gboolean _area_enter_notify_callback(GtkWidget *widget, GdkEventCrossing *event, gpointer user_data)
int default_colorspace(dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece)
static void tab_switch_callback(GtkNotebook *notebook, GtkWidget *page, guint page_num, gpointer user_data)
void gui_cleanup(struct dt_iop_module_t *self)
static gboolean _is_identity(dt_iop_rgbcurve_params_t *p, rgbcurve_channel_t channel)
static gboolean _area_button_press_callback(GtkWidget *widget, GdkEventButton *event, dt_iop_module_t *self)
void init_presets(dt_iop_module_so_t *self)
static gboolean _area_scrolled_callback(GtkWidget *widget, GdkEventScroll *event, dt_iop_module_t *self)
#define RGBCURVE_DEFAULT_STEP
void cleanup_pipe(struct dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
static gboolean _area_motion_notify_callback(GtkWidget *widget, GdkEventMotion *event, dt_iop_module_t *self)
float(* _curve_table_ptr)[0x10000]
static float _curve_to_mouse(const float x, const float zoom_factor, const float offset)
static int _add_node_from_picker(dt_iop_rgbcurve_params_t *p, const float *const in, const float increment, const int ch, const dt_iop_order_iccprofile_info_t *const work_profile)
static gboolean _area_resized_callback(GtkWidget *widget, GdkEvent *event, gpointer user_data)
#define DT_IOP_RGBCURVE_MAXNODES
void color_picker_apply(dt_iop_module_t *self, GtkWidget *picker, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
static gboolean _move_point_internal(dt_iop_module_t *self, GtkWidget *widget, float dx, float dy, guint state)
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]
const float uint32_t state[4]
lib_colorpicker_sample_statistics scope
dt_dev_request_flags_t request_histogram
struct dt_iop_module_t *void * data
struct dt_develop_t::@13 color_picker
Authoritative darkroom color-picker state.
struct dt_dev_pixelpipe_t * pipe
dt_dev_request_colorpick_flags_t request_color_pick
dt_iop_params_t * default_params
struct dt_iop_module_gui_t * gui
struct dt_develop_t * dev
uint32_t histogram_max[4]
dt_aligned_pixel_t picked_output_color
dt_aligned_pixel_t picked_color_min
dt_aligned_pixel_t picked_color_max
int histogram_middle_grey
dt_aligned_pixel_t picked_color
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_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_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
RGB -> XYZ (D50), row-major. matrix_in[1][*] is the luminance row. NaN in [0][0] marks the whole prof...
int curve_changed[DT_IOP_RGBCURVE_MAX_CHANNELS]
dt_iop_rgbcurve_params_t params
dt_draw_curve_t * curve[DT_IOP_RGBCURVE_MAX_CHANNELS]
float unbounded_coeffs[DT_IOP_RGBCURVE_MAX_CHANNELS][3]
dt_colorspaces_color_profile_type_t type_work
float table[DT_IOP_RGBCURVE_MAX_CHANNELS][0x10000]
GtkWidget * cmb_preserve_colors
GtkWidget * chk_compensate_middle_grey
GtkWidget * colorpicker_set_values
int minmax_curve_type[DT_IOP_RGBCURVE_MAX_CHANNELS]
dt_draw_curve_t * minmax_curve[DT_IOP_RGBCURVE_MAX_CHANNELS]
rgbcurve_channel_t channel
GtkNotebook * channel_tabs
int minmax_curve_nodes[DT_IOP_RGBCURVE_MAX_CHANNELS]
int curve_num_nodes[DT_IOP_RGBCURVE_MAX_CHANNELS]
dt_iop_rgb_norms_t preserve_colors
int curve_type[DT_IOP_RGBCURVE_MAX_CHANNELS]
gboolean compensate_middle_grey
dt_iop_rgbcurve_autoscale_t curve_autoscale
dt_iop_rgbcurve_node_t curve_nodes[DT_IOP_RGBCURVE_MAX_CHANNELS][20]
Region of interest passed through the pixelpipe.
#define __DT_CLONE_TARGETS__