63#define DT_GUI_CURVE_EDITOR_INSET DT_PIXEL_APPLY_DPI(1)
64#define DT_IOP_RGBCURVE_RES 256
65#define DT_IOP_RGBCURVE_MAXNODES 20
66#define DT_IOP_RGBCURVE_MIN_X_DISTANCE 0.0025f
69#define DT_IOP_COLOR_ICC_LEN 512
165 _(
"corrective and creative"),
166 _(
"linear, RGB, display-referred"),
167 _(
"non-linear, RGB"),
168 _(
"linear, RGB, display-referred"));
174 memset(&
p, 0,
sizeof(
p));
182 p.compensate_middle_grey = 1;
183 p.preserve_colors = 1;
185 float linear_ab[7] = { 0.0, 0.08, 0.3, 0.5, 0.7, 0.92, 1.0 };
210 float linear_L[7] = { 0.0, 0.08, 0.17, 0.50, 0.83, 0.92, 1.0 };
244 for(
int k = 1;
k < 6;
k++)
246 for(
int k = 1;
k < 6;
k++)
257 for(
int k = 1;
k < 6;
k++)
259 for(
int k = 1;
k < 6;
k++)
294 return (
x - offset) * zoom_factor;
299 return (
x / zoom_factor) + offset;
305 switch(
p->curve_autoscale)
308 if(
p->compensate_middle_grey && !
IS_NULL_PTR(work_profile))
310 for(
int c = 0; c < 3; c++)
out[c] = dt_ioppr_compensate_middle_grey(in[c], work_profile);
314 for(
int c = 0; c < 3; c++)
out[c] = in[c];
320 = (work_profile) ? dt_ioppr_get_rgb_matrix_luminance(in,
323 work_profile->unbounded_coeffs_in,
327 if(
p->compensate_middle_grey && !
IS_NULL_PTR(work_profile))
329 out[0] = dt_ioppr_compensate_middle_grey(val, work_profile);
340 for(
int c = 0; c < 3; c++)
out[c] = CLAMP(
out[c], 0.0f, 1.0f);
352 for(
int k=0;
k<
p->curve_num_nodes[channel];
k++)
353 if(
p->curve_nodes[channel][
k].x !=
p->curve_nodes[channel][
k].y)
return FALSE;
363 if(w ==
g->autoscale)
385 else if(w ==
g->chk_compensate_middle_grey)
393 for(
int k = 0;
k <
p->curve_num_nodes[
ch];
k++)
395 if(
p->compensate_middle_grey)
398 p->curve_nodes[
ch][
k].x = dt_ioppr_compensate_middle_grey(
p->curve_nodes[
ch][
k].x, work_profile);
399 p->curve_nodes[
ch][
k].y = dt_ioppr_compensate_middle_grey(
p->curve_nodes[
ch][
k].y, work_profile);
404 p->curve_nodes[
ch][
k].x = dt_ioppr_uncompensate_middle_grey(
p->curve_nodes[
ch][
k].x, work_profile);
405 p->curve_nodes[
ch][
k].y = dt_ioppr_uncompensate_middle_grey(
p->curve_nodes[
ch][
k].y, work_profile);
433 gtk_widget_queue_draw(GTK_WIDGET(
g->area));
444 gtk_widget_queue_draw(self->
widget);
450 GtkAllocation allocation;
451 gtk_widget_get_allocation(widget, &allocation);
452 r.width = allocation.width;
453 r.height = allocation.width;
454 gtk_widget_get_preferred_size(widget, &
r, NULL);
461 if(curve_nodes[0].
x >
x)
465 for(
int k = 1;
k < *nodes;
k++)
467 if(curve_nodes[
k].
x >
x)
474 if(selected == -1) selected = *nodes;
475 for(
int i = *nodes;
i > selected;
i--)
477 curve_nodes[
i].
x = curve_nodes[
i - 1].
x;
478 curve_nodes[
i].
y = curve_nodes[
i - 1].
y;
481 curve_nodes[selected].
x =
x;
482 curve_nodes[selected].
y = y;
495 val = (work_profile) ? dt_ioppr_get_rgb_matrix_luminance(in,
498 work_profile->unbounded_coeffs_in,
505 if(
p->compensate_middle_grey && !
IS_NULL_PTR(work_profile))
506 y =
x = dt_ioppr_compensate_middle_grey(val, work_profile);
523 if(picker ==
g->colorpicker_set_values)
528 const int ch =
g->channel;
532 p->curve_num_nodes[
ch] =
d->curve_num_nodes[
ch];
533 p->curve_type[
ch] =
d->curve_type[
ch];
536 p->curve_nodes[
ch][
k].x =
d->curve_nodes[
ch][
k].x;
537 p->curve_nodes[
ch][
k].y =
d->curve_nodes[
ch][
k].y;
541 int picker_set_upper_lower;
543 picker_set_upper_lower = 1;
545 picker_set_upper_lower = -1;
547 picker_set_upper_lower = 0;
550 const float increment = 0.05f * picker_set_upper_lower;
556 if(
p->curve_num_nodes[
ch] == 5)
558 p->curve_nodes[
ch][1].x - increment + (
p->curve_nodes[
ch][3].x -
p->curve_nodes[
ch][1].x) / 2.f,
559 p->curve_nodes[
ch][1].y + increment + (
p->curve_nodes[
ch][3].y -
p->curve_nodes[
ch][1].y) / 2.f);
570 gboolean point_valid =
TRUE;
574 if((selected > 0 &&
x - curve[selected - 1].
x <= min_dist)
575 || (selected < nodes - 1 && curve[selected + 1].
x -
x <= min_dist))
580 if((selected > 0 && (curve[selected - 1].
x >=
x)) || (selected < nodes - 1 && (curve[selected + 1].
x <=
x)))
593 const int ch =
g->channel;
596 const float new_x = CLAMP(curve[
g->selected].
x + dx, 0.0f, 1.0f);
597 const float new_y = CLAMP(curve[
g->selected].
y + dy, 0.0f, 1.0f);
599 gtk_widget_queue_draw(widget);
603 curve[
g->selected].
x = new_x;
604 curve[
g->selected].
y = new_y;
612#define RGBCURVE_DEFAULT_STEP (0.001f)
624 if(
g->selected < 0)
return TRUE;
645 if(
g->selected < 0)
return FALSE;
649 float dx = 0.0f, dy = 0.0f;
650 if(
key == GDK_KEY_Up)
655 else if(
key == GDK_KEY_Down)
660 else if(
key == GDK_KEY_Right)
665 else if(
key == GDK_KEY_Left)
671 if(!handled)
return FALSE;
677#undef RGBCURVE_DEFAULT_STEP
681 gtk_widget_queue_draw(widget);
687 gtk_widget_queue_draw(widget);
697 const int ch =
g->channel;
698 const int nodes =
p->curve_num_nodes[
ch];
699 const int autoscale =
p->curve_autoscale;
702 if(
g->minmax_curve_type[
ch] !=
p->curve_type[
ch] ||
g->minmax_curve_nodes[
ch] !=
p->curve_num_nodes[
ch])
706 g->minmax_curve_nodes[
ch] =
p->curve_num_nodes[
ch];
707 g->minmax_curve_type[
ch] =
p->curve_type[
ch];
708 for(
int k = 0;
k <
p->curve_num_nodes[
ch];
k++)
713 for(
int k = 0;
k <
p->curve_num_nodes[
ch];
k++)
720 const float xm = curve_nodes[nodes - 1].
x;
722 const float x[4] = { 0.7f * xm, 0.8f * xm, 0.9f * xm, 1.0f * xm };
731 GtkAllocation allocation;
732 gtk_widget_get_allocation(widget, &allocation);
733 int width = allocation.width,
height = allocation.height;
735 cairo_t *cr = cairo_create(cst);
738 cairo_set_source_rgb(cr, .2, .2, .2);
741 cairo_translate(cr, inset, inset);
747 cairo_set_source_rgb(cr, .1, .1, .1);
751 cairo_set_source_rgb(cr, .3, .3, .3);
757 cairo_set_source_rgb(cr, .1, .1, .1);
759 cairo_translate(cr, 0,
height);
761 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);
763 const double dashed[] = { 4.0, 4.0 };
764 const int len =
sizeof(dashed) /
sizeof(dashed[0]);
765 cairo_set_dash(cr, dashed, len, 0);
766 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);
767 cairo_set_dash(cr, dashed, 0, 0);
781 cairo_set_source_rgb(cr, 0.6, 0.6, 0.6);
783 for(
int k = 0;
k < nodes;
k++)
797 cairo_set_source_rgb(cr, .9, .9, .9);
810 const gboolean is_linear =
FALSE;
819 hist_max = logf(1.0 + hist_max);
823 cairo_push_group_with_content(cr, CAIRO_CONTENT_COLOR);
828 cairo_set_operator(cr, CAIRO_OPERATOR_ADD);
843 cairo_pop_group_to_source(cr);
844 cairo_paint_with_alpha(cr, 0.2);
861 for(; samples; samples = g_slist_next(samples))
866 for(
int k = 0;
k < 3;
k++)
872 picker_mean[3] = picker_min[3] = picker_max[3] = 1.f;
875 work_profile,
"rgb curve");
890 cairo_set_source_rgba(cr, 0.5, 0.7, 0.5, 0.15);
891 cairo_rectangle(cr,
width * picker_min[
ch], 0,
width * fmax(picker_max[
ch] - picker_min[
ch], 0.0f),
894 cairo_set_source_rgba(cr, 0.5, 0.7, 0.5, 0.5);
895 cairo_move_to(cr,
width * picker_mean[
ch], 0);
908 pango_font_description_set_weight(desc, PANGO_WEIGHT_BOLD);
909 pango_font_description_set_absolute_size(desc, PANGO_SCALE);
910 layout = pango_cairo_create_layout(cr);
911 pango_layout_set_font_description(layout, desc);
918 snprintf(text,
sizeof(text),
"100.00 / 100.00 ( +100.00)");
919 pango_layout_set_text(layout, text, -1);
920 pango_layout_get_pixel_extents(layout, &ink, NULL);
921 pango_font_description_set_absolute_size(desc,
width * 1.0 / ink.width * PANGO_SCALE);
922 pango_layout_set_font_description(layout, desc);
928 cairo_set_source_rgba(cr, 0.7, 0.5, 0.5, 0.33);
929 cairo_rectangle(cr,
width * picker_min[
ch], 0,
width * fmax(picker_max[
ch] - picker_min[
ch], 0.0f),
932 cairo_set_source_rgba(cr, 0.9, 0.7, 0.7, 0.5);
933 cairo_move_to(cr,
width * picker_mean[
ch], 0);
939 snprintf(text,
sizeof(text),
"%.1f \342\206\222 %.1f", picker_mean[
ch] * 255.f, picker_min[
ch] * 255.f);
941 cairo_set_source_rgb(cr, 0.1, 0.1, 0.1);
943 pango_layout_set_text(layout, text, -1);
944 pango_layout_get_pixel_extents(layout, &ink, NULL);
945 cairo_move_to(cr, 0.02f *
width, -0.94 *
height - ink.height - ink.y);
946 pango_cairo_show_layout(cr, layout);
948 pango_font_description_free(desc);
949 g_object_unref(layout);
961 pango_font_description_set_weight(desc, PANGO_WEIGHT_BOLD);
962 pango_font_description_set_absolute_size(desc, PANGO_SCALE);
963 layout = pango_cairo_create_layout(cr);
964 pango_layout_set_font_description(layout, desc);
967 snprintf(text,
sizeof(text),
"100.00 / 100.00 ( +100.00)");
968 pango_layout_set_text(layout, text, -1);
969 pango_layout_get_pixel_extents(layout, &ink, NULL);
970 pango_font_description_set_absolute_size(desc,
width * 1.0 / ink.width * PANGO_SCALE);
971 pango_layout_set_font_description(layout, desc);
973 const float min_scale_value = 0.0f;
974 const float max_scale_value = 255.0f;
976 const float x_node_value = curve_nodes[
g->selected].
x * (max_scale_value - min_scale_value) + min_scale_value;
977 const float y_node_value = curve_nodes[
g->selected].
y * (max_scale_value - min_scale_value) + min_scale_value;
978 const float d_node_value = y_node_value - x_node_value;
979 snprintf(text,
sizeof(text),
"%.1f / %.1f ( %+.1f)", x_node_value, y_node_value, d_node_value);
981 cairo_set_source_rgb(cr, 0.1, 0.1, 0.1);
982 pango_layout_set_text(layout, text, -1);
983 pango_layout_get_pixel_extents(layout, &ink, NULL);
984 cairo_move_to(cr, 0.98f *
width - ink.width - ink.x, -0.02 *
height - ink.height - ink.y);
985 pango_cairo_show_layout(cr, layout);
987 pango_font_description_free(desc);
988 g_object_unref(layout);
991 cairo_set_source_rgb(cr, .9, .9, .9);
1001 cairo_set_source_rgb(cr, .9, .9, .9);
1004 cairo_move_to(cr, 0, -
height * y_offset);
1029 cairo_set_source_surface(crf, cst, 0, 0);
1031 cairo_surface_destroy(cst);
1042 const int ch =
g->channel;
1043 const int nodes =
p->curve_num_nodes[
ch];
1049 GtkAllocation allocation;
1050 gtk_widget_get_allocation(widget, &allocation);
1051 const int height = allocation.height - 2 * inset,
width = allocation.width - 2 * inset;
1053 const double old_m_x =
g->mouse_x;
1054 const double old_m_y =
g->mouse_y;
1056 g->mouse_x = CLAMP(event->x - inset, 0,
width) / (float)
width;
1057 g->mouse_y = 1.0 - CLAMP(event->y - inset, 0,
height) / (float)
height;
1059 const float mx =
g->mouse_x;
1060 const float my =
g->mouse_y;
1064 if(event->state & GDK_BUTTON1_MASK)
1067 if(
g->selected >= 0)
1070 const float translate_mouse_x
1072 const float translate_mouse_y
1075 const float dx =
_mouse_to_curve(
g->mouse_x - translate_mouse_x,
g->zoom_factor,
g->offset_x)
1077 const float dy =
_mouse_to_curve(
g->mouse_y - translate_mouse_y,
g->zoom_factor,
g->offset_y)
1083 else if(nodes < DT_IOP_RGBCURVE_MAXNODES && g->selected >= -1)
1087 g->selected =
_add_node(curve_nodes, &
p->curve_num_nodes[
ch], linx, liny);
1094 float min = .04f * .04f;
1096 for(
int k = 0;
k < nodes;
k++)
1108 g->selected = nearest;
1111 if(
g->selected >= 0) gtk_widget_grab_focus(widget);
1112 gtk_widget_queue_draw(widget);
1122 const int ch =
g->channel;
1123 const int autoscale =
p->curve_autoscale;
1124 const int nodes =
p->curve_num_nodes[
ch];
1127 if(event->button == 1)
1129 if(event->type == GDK_BUTTON_PRESS &&
dt_modifier_is(event->state, GDK_CONTROL_MASK)
1130 && nodes < DT_IOP_RGBCURVE_MAXNODES && g->selected == -1)
1134 GtkAllocation allocation;
1135 gtk_widget_get_allocation(widget, &allocation);
1136 const int width = allocation.width - 2 * inset;
1137 const int height = allocation.height - 2 * inset;
1139 g->mouse_x = CLAMP(event->x - inset, 0,
width) / (float)
width;
1140 g->mouse_y = 1.0 - CLAMP(event->y - inset, 0,
height) / (float)
height;
1142 const float mx =
g->mouse_x;
1147 if(curve_nodes[0].
x > mx)
1151 for(
int k = 1;
k < nodes;
k++)
1153 if(curve_nodes[
k].
x > mx)
1160 if(selected == -1) selected = nodes;
1165 if(y >= 0.0f && y <= 1.0f)
1168 selected =
_add_node(curve_nodes, &
p->curve_num_nodes[
ch], linx, y);
1171 const float min = .04f * .04f;
1172 for(
int k = 0;
k < nodes;
k++)
1174 const float other_y =
_curve_to_mouse(curve_nodes[
k].y,
g->zoom_factor,
g->offset_y);
1175 const float dist = (y - other_y) * (y - other_y);
1176 if(
dist <
min)
g->selected = selected;
1181 gtk_widget_queue_draw(self->
widget);
1186 else if(event->type == GDK_2BUTTON_PRESS)
1192 p->curve_num_nodes[
ch] =
d->curve_num_nodes[
ch];
1193 p->curve_type[
ch] =
d->curve_type[
ch];
1194 for(
int k = 0;
k <
d->curve_num_nodes[
ch];
k++)
1196 p->curve_nodes[
ch][
k].x =
d->curve_nodes[
ch][
k].x;
1197 p->curve_nodes[
ch][
k].y =
d->curve_nodes[
ch][
k].y;
1203 gtk_widget_queue_draw(self->
widget);
1214 gtk_widget_queue_draw(self->
widget);
1220 else if(event->button == 3 &&
g->selected >= 0)
1222 if(
g->selected == 0 ||
g->selected == nodes - 1)
1224 const float reset_value =
g->selected == 0 ? 0.f : 1.f;
1225 curve_nodes[
g->selected].
y = curve_nodes[
g->selected].
x = reset_value;
1228 gtk_widget_queue_draw(self->
widget);
1232 for(
int k =
g->selected;
k < nodes - 1;
k++)
1234 curve_nodes[
k].
x = curve_nodes[
k + 1].
x;
1235 curve_nodes[
k].
y = curve_nodes[
k + 1].
y;
1237 curve_nodes[nodes - 1].
x = curve_nodes[nodes - 1].
y = 0;
1239 p->curve_num_nodes[
ch]--;
1242 gtk_widget_queue_draw(self->
widget);
1255 g->offset_x =
g->offset_y = 0.f;
1256 g->zoom_factor = 1.f;
1260 gtk_widget_queue_draw(self->
widget);
1270 g->mouse_x =
g->mouse_y = -1.0;
1272 g->offset_x =
g->offset_y = 0.f;
1273 g->zoom_factor = 1.f;
1285 g->minmax_curve_nodes[
ch] =
p->curve_num_nodes[
ch];
1286 g->minmax_curve_type[
ch] =
p->curve_type[
ch];
1287 for(
int k = 0;
k <
p->curve_num_nodes[
ch];
k++)
1296 gtk_widget_set_tooltip_text(
g->autoscale, _(
"choose between linked and independent channels."));
1300 g->channel_tabs = GTK_NOTEBOOK(gtk_notebook_new());
1304 g_signal_connect(G_OBJECT(
g->channel_tabs),
"switch_page", G_CALLBACK(
tab_switch_callback), self);
1305 gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(
g->channel_tabs),
TRUE,
TRUE, 0);
1306 gtk_box_pack_start(GTK_BOX(hbox), gtk_grid_new(),
TRUE,
TRUE, 0);
1310 gtk_widget_set_tooltip_text(
g->colorpicker, _(
"pick GUI color from image\nctrl+click or right-click to select an area"));
1316 gtk_widget_set_tooltip_text(
g->colorpicker_set_values, _(
"create a curve based on an area from the image\n"
1317 "drag to create a flat curve\n"
1318 "ctrl+drag to create a positive curve\n"
1319 "shift+drag to create a negative curve"));
1323 gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(hbox),
TRUE,
TRUE, 0);
1325 g->area = GTK_DRAWING_AREA(gtk_drawing_area_new());
1326 gtk_widget_set_hexpand(GTK_WIDGET(
g->area),
TRUE);
1327 g_object_set_data(G_OBJECT(
g->area),
"iop-instance", self);
1328 gtk_box_pack_start(GTK_BOX(vbox),
1330 "plugins/darkroom/rgbcurve/graphheight", 280, 100),
1337 | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK
1338 | GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK);
1339 gtk_widget_set_can_focus(GTK_WIDGET(
g->area),
TRUE);
1359 gtk_box_pack_start(GTK_BOX(self->
widget),
g->interpolator,
TRUE,
TRUE, 0);
1360 gtk_widget_set_tooltip_text(
g->interpolator,
1361 _(
"change this method if you see oscillations or cusps in the curve\n"
1362 "- cubic spline is better to produce smooth curves but oscillates when nodes are too close\n"
1363 "- centripetal is better to avoids cusps and oscillations with close nodes but is less smooth\n"
1364 "- monotonic is better for accuracy of pure analytical functions (log, gamma, exp)\n"));
1368 gtk_widget_set_tooltip_text(
g->chk_compensate_middle_grey, _(
"compensate middle gray"));
1371 gtk_widget_set_tooltip_text(
g->cmb_preserve_colors, _(
"method to preserve colors when applying contrast"));
1381 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(
g->chk_compensate_middle_grey),
p->compensate_middle_grey);
1389 gtk_widget_queue_draw(self->
widget);
1408 piece->
data = (
void *)
d;
1440 module->request_histogram |= (DT_REQUEST_ON);
1445 d->curve_nodes[1][1].x =
d->curve_nodes[1][1].y =
1446 d->curve_nodes[2][1].x =
d->curve_nodes[2][1].y = 1.0;
1448 module->histogram_middle_grey = d->compensate_middle_grey;
1462 if(
d->type_work == work_profile->
type && strcmp(
d->filename_work, work_profile->
filename) == 0)
return;
1465 if(!
IS_NULL_PTR(work_profile) &&
d->params.compensate_middle_grey)
1467 d->type_work = work_profile->
type;
1468 g_strlcpy(
d->filename_work, work_profile->
filename,
sizeof(
d->filename_work));
1472 for(
int k = 0;
k <
d->params.curve_num_nodes[
ch];
k++)
1474 curve_nodes[
ch][
k].
x = dt_ioppr_uncompensate_middle_grey(
d->params.curve_nodes[
ch][
k].x, work_profile);
1475 curve_nodes[
ch][
k].
y = dt_ioppr_uncompensate_middle_grey(
d->params.curve_nodes[
ch][
k].y, work_profile);
1490 if(
d->curve_changed[
ch])
1494 for(
int k = 0;
k <
d->params.curve_num_nodes[
ch];
k++)
1499 for(
int k = 0;
k <
d->params.curve_num_nodes[
ch];
k++)
1509 const float xm_L = curve_nodes[
ch][
d->params.curve_num_nodes[
ch] - 1].
x;
1510 const float x_L[4] = { 0.7f * xm_L, 0.8f * xm_L, 0.9f * xm_L, 1.0f * xm_L };
1511 const float y_L[4] = {
d->table[
ch][CLAMP((
int)(x_L[0] * 0x10000ul), 0, 0xffff)],
1512 d->table[
ch][CLAMP((
int)(x_L[1] * 0x10000ul), 0, 0xffff)],
1513 d->table[
ch][CLAMP((
int)(x_L[2] * 0x10000ul), 0, 0xffff)],
1514 d->table[
ch][CLAMP((
int)(x_L[3] * 0x10000ul), 0, 0xffff)] };
1531 d->curve_changed[
ch]
1532 = (
d->params.curve_type[
ch] !=
p->curve_type[
ch] ||
d->params.curve_nodes[
ch] !=
p->curve_nodes[
ch]);
1538 d->filename_work[0] =
'\0';
1549 const float *
const restrict in = (
float*)ivoid;
1550 float *
const restrict
out = (
float*)
ovoid;
1563 const int autoscale =
d->params.curve_autoscale;
1567 for(
int y = 0; y < 4*npixels; y += 4)
1571 out[y+0] = (in[y+0] < xm_L) ? table[
DT_IOP_RGBCURVE_R][CLAMP((
int)(in[y+0] * 0x10000ul), 0, 0xffff)]
1573 out[y+1] = (in[y+1] < xm_g) ? table[
DT_IOP_RGBCURVE_G][CLAMP((
int)(in[y+1] * 0x10000ul), 0, 0xffff)]
1575 out[y+2] = (in[y+2] < xm_b) ? table[
DT_IOP_RGBCURVE_B][CLAMP((
int)(in[y+2] * 0x10000ul), 0, 0xffff)]
1582 for(
int c = 0; c < 3; c++)
1584 out[y+c] = (in[y+c] < xm_L) ? table[
DT_IOP_RGBCURVE_R][CLAMP((
int)(in[y+c] * 0x10000ul), 0, 0xffff)]
1591 const float lum =
dt_rgb_norm(in + y,
d->params.preserve_colors, work_profile);
1594 const float curve_lum = (lum < xm_L)
1597 ratio = curve_lum / lum;
1599 for(
size_t c = 0; c < 3; c++)
1601 out[y+c] = (ratio * in[y+c]);
1611#undef DT_GUI_CURVE_EDITOR_INSET
1612#undef DT_IOP_RGBCURVE_RES
1613#undef DT_IOP_RGBCURVE_MAXNODES
1614#undef DT_IOP_RGBCURVE_MIN_X_DISTANCE
1615#undef DT_IOP_COLOR_ICC_LEN
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)
static void set_color(cairo_t *cr, GdkRGBA color)
@ DEVELOP_BLEND_CS_RGB_DISPLAY
static const dt_aligned_pixel_simd_t const dt_adaptation_t const float p
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
dt_colorspaces_color_profile_type_t
static const float const float const float min
const dt_colormatrix_t dt_aligned_pixel_t out
typedef void((*dt_cache_allocate_t)(void *userdata, dt_cache_entry_t *entry))
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...
#define dt_free_align(ptr)
static void * dt_calloc_align(size_t size)
#define DT_MODULE_INTROSPECTION(MODVER, PARAMSTYPE)
#define __DT_CLONE_TARGETS__
#define __OMP_PARALLEL_FOR__(...)
static gboolean dt_modifier_is(const GdkModifierType state, const GdkModifierType desired_modifier_mask)
#define IS_NULL_PTR(p)
C is way too permissive with !=, == and if(var) checks, which can mean too many things depending on w...
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)
gboolean dt_dev_pixelpipe_has_preview_output(const dt_develop_t *dev, const dt_dev_pixelpipe_t *pipe, const dt_iop_roi_t *roi)
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 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)
void dtgtk_cairo_paint_colorpicker(cairo_t *cr, gint x, gint y, gint w, gint h, gint flags, void *data)
static guint dt_keys_mainpad_alternatives(const guint key_val)
Remap keypad keys to usual mainpad ones.
gboolean dt_gui_get_scroll_delta(const GdkEventScroll *event, gdouble *delta)
GtkWidget * dt_ui_resizable_drawing_area(GtkWidget *area, char *config_str, int default_height, int min_height)
Make a self-drawing widget (typically a GtkDrawingArea graph or scope) vertically resizable.
GtkWidget * dt_ui_notebook_page(GtkNotebook *notebook, const char *text, const char *tooltip)
GdkModifierType dt_key_modifier_state()
static cairo_surface_t * dt_cairo_image_surface_create(cairo_format_t format, int width, int height)
#define DT_GUI_BOX_SPACING
#define DT_PIXEL_APPLY_DPI(value)
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, const dt_develop_blend_colorspace_t blend_cst)
void dt_gui_throttle_cancel(gpointer source)
void dt_gui_throttle_queue(gpointer source, dt_gui_throttle_callback_t callback, gpointer user_data)
void dt_iop_throttled_history_update(gpointer data)
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
#define IOP_GUI_ALLOC(module)
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 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)
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)
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)
dt_iop_order_iccprofile_info_t * dt_ioppr_get_iop_work_profile_info(struct dt_iop_module_t *module, GList *iop_list)
static dt_aligned_pixel_t float *const const float unbounded_coeffs[3][3]
float *const restrict const size_t k
float *const restrict const size_t const size_t ch
float dt_aligned_pixel_t[4]
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)
struct _GtkWidget GtkWidget
const float uint32_t state[4]
struct dt_gui_gtk_t * gui
struct dt_bauhaus_t * bauhaus
struct dt_develop_t * develop
PangoFontDescription * pango_font_desc
lib_colorpicker_sample_statistics scope
dt_dev_request_flags_t request_histogram
struct dt_iop_module_t *void * data
struct dt_develop_t::@19 color_picker
Authoritative darkroom color-picker state.
struct dt_dev_pixelpipe_t * pipe
GModule *dt_dev_operation_t op
dt_dev_request_colorpick_flags_t request_color_pick
dt_iop_params_t * default_params
struct dt_develop_t * dev
dt_iop_gui_data_t * gui_data
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
dt_colorspaces_color_profile_type_t type
char filename[DT_IOP_COLOR_ICC_LEN]
dt_colormatrix_t matrix_in
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.