72#define DT_IOP_LOWLIGHT_INSET DT_PIXEL_APPLY_DPI(5)
73#define DT_IOP_LOWLIGHT_RES 64
74#define DT_IOP_LOWLIGHT_BANDS 6
75#define DT_IOP_LOWLIGHT_LUT_RES 0x10000
109 return _(
"lowlight vision");
116 _(
"non-linear, Lab, display-referred"),
118 _(
"non-linear, Lab, display-referred"));
149 return lut[bin1] *
f +
lut[bin0] * (1. -
f);
160 const float c = 0.5f;
169 for(
size_t k = 0;
k < (size_t)roi_out->
width * roi_out->
height;
k++)
171 float *in = (
float *)
i +
ch *
k;
172 float *
out = (
float *)o +
ch *
k;
183 V =
XYZ[1] * (1.33f * (1.0f + (
XYZ[1] +
XYZ[2]) /
XYZ[0]) - 1.68f);
192 V = fminf(1.0f, fmaxf(0.0f, c *
V));
195 w =
lookup(
d->lut, in[0] / 100.f);
197 XYZ_s[0] =
V * XYZ_sw[0];
198 XYZ_s[1] =
V * XYZ_sw[1];
199 XYZ_s[2] =
V * XYZ_sw[2];
201 XYZ[0] = w *
XYZ[0] + (1.0f - w) * XYZ_s[0];
202 XYZ[1] = w *
XYZ[1] + (1.0f - w) * XYZ_s[1];
203 XYZ[2] = w *
XYZ[2] + (1.0f - w) * XYZ_s[2];
223 d->blueness =
p->blueness;
230 piece->
data = (
void *)
d;
256 gtk_widget_queue_draw(self->
widget);
274 p.transition_x[0] = 0.000000;
275 p.transition_x[1] = 0.200000;
276 p.transition_x[2] = 0.400000;
277 p.transition_x[3] = 0.600000;
278 p.transition_x[4] = 0.800000;
279 p.transition_x[5] = 1.000000;
281 p.transition_y[0] = 1.000000;
282 p.transition_y[1] = 1.000000;
283 p.transition_y[2] = 1.000000;
284 p.transition_y[3] = 1.000000;
285 p.transition_y[4] = 1.000000;
286 p.transition_y[5] = 1.000000;
292 p.transition_x[0] = 0.000000;
293 p.transition_x[1] = 0.200000;
294 p.transition_x[2] = 0.400000;
295 p.transition_x[3] = 0.600000;
296 p.transition_x[4] = 0.800000;
297 p.transition_x[5] = 1.000000;
299 p.transition_y[0] = 0.600000;
300 p.transition_y[1] = 0.800000;
301 p.transition_y[2] = 0.950000;
302 p.transition_y[3] = 0.980000;
303 p.transition_y[4] = 1.000000;
304 p.transition_y[5] = 1.000000;
310 p.transition_x[0] = 0.000000;
311 p.transition_x[1] = 0.200000;
312 p.transition_x[2] = 0.400000;
313 p.transition_x[3] = 0.600000;
314 p.transition_x[4] = 0.800000;
315 p.transition_x[5] = 1.000000;
317 p.transition_y[0] = 0.300000;
318 p.transition_y[1] = 0.500000;
319 p.transition_y[2] = 0.700000;
320 p.transition_y[3] = 0.850000;
321 p.transition_y[4] = 0.970000;
322 p.transition_y[5] = 1.000000;
328 p.transition_x[0] = 0.000000;
329 p.transition_x[1] = 0.200000;
330 p.transition_x[2] = 0.400000;
331 p.transition_x[3] = 0.600000;
332 p.transition_x[4] = 0.800000;
333 p.transition_x[5] = 1.000000;
335 p.transition_y[0] = 0.050000;
336 p.transition_y[1] = 0.200000;
337 p.transition_y[2] = 0.400000;
338 p.transition_y[3] = 0.700000;
339 p.transition_y[4] = 0.920000;
340 p.transition_y[5] = 1.000000;
346 p.transition_x[0] = 0.000000;
347 p.transition_x[1] = 0.200000;
348 p.transition_x[2] = 0.400000;
349 p.transition_x[3] = 0.600000;
350 p.transition_x[4] = 0.800000;
351 p.transition_x[5] = 1.000000;
353 p.transition_y[0] = 0.070000;
354 p.transition_y[1] = 0.100000;
355 p.transition_y[2] = 0.180000;
356 p.transition_y[3] = 0.350000;
357 p.transition_y[4] = 0.750000;
358 p.transition_y[5] = 1.000000;
364 p.transition_x[0] = 0.000000;
365 p.transition_x[1] = 0.200000;
366 p.transition_x[2] = 0.400000;
367 p.transition_x[3] = 0.600000;
368 p.transition_x[4] = 0.800000;
369 p.transition_x[5] = 1.000000;
371 p.transition_y[0] = 0.000000;
372 p.transition_y[1] = 0.450000;
373 p.transition_y[2] = 0.750000;
374 p.transition_y[3] = 0.930000;
375 p.transition_y[4] = 0.990000;
376 p.transition_y[5] = 1.000000;
382 p.transition_x[0] = 0.000000;
383 p.transition_x[1] = 0.200000;
384 p.transition_x[2] = 0.400000;
385 p.transition_x[3] = 0.600000;
386 p.transition_x[4] = 0.800000;
387 p.transition_x[5] = 1.000000;
389 p.transition_y[0] = 0.000000;
390 p.transition_y[1] = 0.150000;
391 p.transition_y[2] = 0.350000;
392 p.transition_y[3] = 0.800000;
393 p.transition_y[4] = 0.970000;
394 p.transition_y[5] = 1.000000;
400 p.transition_x[0] = 0.000000;
401 p.transition_x[1] = 0.150000;
402 p.transition_x[2] = 0.400000;
403 p.transition_x[3] = 0.600000;
404 p.transition_x[4] = 0.800000;
405 p.transition_x[5] = 1.000000;
407 p.transition_y[0] = 0.000000;
408 p.transition_y[1] = 0.020000;
409 p.transition_y[2] = 0.050000;
410 p.transition_y[3] = 0.200000;
411 p.transition_y[4] = 0.550000;
412 p.transition_y[5] = 1.000000;
418 p.transition_x[0] = 0.000000;
419 p.transition_x[1] = 0.200000;
420 p.transition_x[2] = 0.400000;
421 p.transition_x[3] = 0.600000;
422 p.transition_x[4] = 0.800000;
423 p.transition_x[5] = 1.000000;
425 p.transition_y[0] = 0.000000;
426 p.transition_y[1] = 0.000000;
427 p.transition_y[2] = 0.000000;
428 p.transition_y[3] = 0.000000;
429 p.transition_y[4] = 0.000000;
430 p.transition_y[5] = 0.000000;
442 const double mouse_y,
const float rad)
446 const float f = expf(-(mouse_x -
p->transition_x[
k]) * (mouse_x -
p->transition_x[
k]) / (rad * rad));
447 p->transition_y[
k] = (1 -
f) *
p->transition_y[
k] +
f * mouse_y;
465 GtkAllocation allocation;
466 gtk_widget_get_allocation(widget, &allocation);
467 int width = allocation.width,
height = allocation.height;
469 cairo_t *cr = cairo_create(cst);
471 cairo_set_source_rgb(cr, .2, .2, .2);
474 cairo_translate(cr, inset, inset);
479 cairo_set_source_rgb(cr, .1, .1, .1);
483 cairo_set_source_rgb(cr, .3, .3, .3);
489 cairo_set_source_rgb(cr, .1, .1, .1);
493 if(c->mouse_y > 0 || c->dragging)
521 cairo_set_source_rgb(cr, 0.6, 0.6, 0.6);
527 cairo_rel_line_to(cr, -arrw * .5f, 0);
528 cairo_rel_line_to(cr, arrw * .5f, -arrw);
529 cairo_rel_line_to(cr, arrw * .5f, arrw);
530 cairo_close_path(cr);
538 cairo_translate(cr, 0,
height);
543 cairo_set_source_rgba(cr, .7, .7, .7, 1.0);
559 cairo_set_source_rgb(cr, 0.7, 0.7, 0.7);
571 if(c->mouse_y > 0 || c->dragging)
574 cairo_set_source_rgba(cr, .7, .7, .7, .6);
575 cairo_move_to(cr, 0, -
height * c->draw_min_ys[0]);
580 cairo_close_path(cr);
583 cairo_set_source_rgba(cr, .9, .9, .9, .5);
586 const float f =
k - pos;
588 float ht = -
height * (
f * c->draw_ys[
k] + (1 -
f) * c->draw_ys[
k + 1]);
589 cairo_arc(cr, c->mouse_x *
width, ht, c->mouse_radius *
width, 0, 2. *
M_PI);
595 cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
601 pango_font_description_set_weight(desc, PANGO_WEIGHT_BOLD);
602 pango_font_description_set_absolute_size(desc,(.06 *
height) * PANGO_SCALE);
603 layout = pango_cairo_create_layout(cr);
604 pango_layout_set_font_description(layout, desc);
605 cairo_set_source_rgb(cr, .1, .1, .1);
607 pango_layout_set_text(layout, _(
"dark"), -1);
608 pango_layout_get_pixel_extents(layout, &ink, NULL);
609 cairo_move_to(cr, .02 *
width - ink.y, .5 * (
height + ink.width));
611 cairo_rotate(cr, -
M_PI * .5f);
612 pango_cairo_show_layout(cr, layout);
615 pango_layout_set_text(layout, _(
"bright"), -1);
616 pango_layout_get_pixel_extents(layout, &ink, NULL);
617 cairo_move_to(cr, .98 *
width - ink.height, .5 * (
height + ink.width));
619 cairo_rotate(cr, -
M_PI * .5f);
620 pango_cairo_show_layout(cr, layout);
624 pango_layout_set_text(layout, _(
"day vision"), -1);
625 pango_layout_get_pixel_extents(layout, &ink, NULL);
626 cairo_move_to(cr, .5 * (
width - ink.width), .08 *
height - ink.height);
627 pango_cairo_show_layout(cr, layout);
629 pango_layout_set_text(layout, _(
"night vision"), -1);
630 pango_layout_get_pixel_extents(layout, &ink, NULL);
631 cairo_move_to(cr, .5 * (
width - ink.width), .97 *
height - ink.height);
632 pango_cairo_show_layout(cr, layout);
634 pango_font_description_free(desc);
635 g_object_unref(layout);
637 cairo_set_source_surface(crf, cst, 0, 0);
639 cairo_surface_destroy(cst);
649 GtkAllocation allocation;
650 gtk_widget_get_allocation(widget, &allocation);
651 int height = allocation.height - 2 * inset,
width = allocation.width - 2 * inset;
652 if(!c->dragging) c->mouse_x = CLAMP(event->x - inset, 0,
width) / (float)
width;
653 c->mouse_y = 1.0 - CLAMP(event->y - inset, 0,
height) / (float)
height;
659 const float mx = CLAMP(event->x - inset, 0,
width) / (float)
width;
662 const float minx =
p->transition_x[c->x_move - 1] + 0.001f;
663 const float maxx =
p->transition_x[c->x_move + 1] - 0.001f;
664 p->transition_x[c->x_move] = fminf(maxx, fmaxf(minx, mx));
671 gtk_widget_queue_draw(widget);
674 else if(event->y >
height)
677 float dist = fabs(
p->transition_x[0] - c->mouse_x);
680 float d2 = fabs(
p->transition_x[
k] - c->mouse_x);
687 gtk_widget_queue_draw(widget);
692 gtk_widget_queue_draw(widget);
700 if(event->button == 1 && event->type == GDK_2BUTTON_PRESS)
707 p->transition_x[
k] =
d->transition_x[
k];
708 p->transition_y[
k] =
d->transition_y[
k];
711 gtk_widget_queue_draw(self->
widget);
713 else if(event->button == 1)
718 GtkAllocation allocation;
719 gtk_widget_get_allocation(widget, &allocation);
720 int height = allocation.height - 2 * inset,
width = allocation.width - 2 * inset;
723 c->mouse_pick -= 1.0 - CLAMP(event->y - inset, 0,
height) / (float)
height;
732 if(event->button == 1)
746 if(!c->dragging) c->mouse_y = -1.0;
747 gtk_widget_queue_draw(widget);
760 gtk_widget_queue_draw(widget);
778 c->mouse_x = c->mouse_y = c->mouse_pick = -1.0;
786 c->area = GTK_DRAWING_AREA(gtk_drawing_area_new());
787 gtk_widget_set_hexpand(GTK_WIDGET(c->area),
TRUE);
788 g_object_set_data(G_OBJECT(c->area),
"iop-instance", self);
789 gtk_box_pack_start(GTK_BOX(self->
widget),
791 "plugins/darkroom/lowlight/graphheight", 280, 100),
795 | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK
796 | GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK);
797 g_signal_connect(G_OBJECT(c->area),
"draw", G_CALLBACK(
lowlight_draw), self);
802 g_signal_connect(G_OBJECT(c->area),
"scroll-event", G_CALLBACK(
lowlight_scrolled), self);
806 gtk_widget_set_tooltip_text(c->scale_blueness, _(
"blueness in shadows"));
static double dist(double x1, double y1, double x2, double y2)
void dt_bauhaus_slider_set(GtkWidget *widget, float pos)
void dt_bauhaus_slider_set_format(GtkWidget *widget, const char *format)
@ DEVELOP_BLEND_CS_RGB_DISPLAY
static const dt_aligned_pixel_simd_t const dt_adaptation_t const float p
static float lookup(read_only image2d_t lut, const float x)
const dt_aligned_pixel_t f
static dt_aligned_pixel_t XYZ
const dt_colormatrix_t dt_aligned_pixel_t out
typedef void((*dt_cache_allocate_t)(void *userdata, dt_cache_entry_t *entry))
#define dt_free_align(ptr)
static void * dt_calloc_align(size_t size)
float dt_aligned_pixel_simd_t __attribute__((vector_size(16), aligned(16)))
Enable aggressive floating-point arithmetic optimizations, in denormals handling. Set through user pr...
#define DT_MODULE_INTROSPECTION(MODVER, PARAMSTYPE)
#define __DT_CLONE_TARGETS__
#define __OMP_PARALLEL_FOR__(...)
#define dt_database_start_transaction(db)
#define dt_database_release_transaction(db)
#define dt_dev_add_history_item(dev, module, enable, redraw)
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_grid(cairo_t *cr, const int num, const int left, const int top, const int right, const int bottom)
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)
gboolean dt_gui_get_scroll_unit_deltas(const GdkEventScroll *event, int *delta_x, int *delta_y)
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.
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)
@ IOP_FLAGS_INCLUDE_IN_STYLES
@ IOP_FLAGS_SUPPORTS_BLENDING
#define IOP_GUI_ALLOC(module)
GtkWidget * dt_bauhaus_slider_from_params(dt_iop_module_t *self, const char *param)
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)
static gboolean lowlight_draw(GtkWidget *widget, cairo_t *crf, gpointer user_data)
static gboolean lowlight_scrolled(GtkWidget *widget, GdkEventScroll *event, gpointer user_data)
static gboolean lowlight_button_press(GtkWidget *widget, GdkEventButton *event, gpointer user_data)
static gboolean lowlight_motion_notify(GtkWidget *widget, GdkEventMotion *event, gpointer user_data)
#define DT_IOP_LOWLIGHT_BANDS
#define DT_IOP_LOWLIGHT_INSET
void init_pipe(struct dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
static gboolean lowlight_button_release(GtkWidget *widget, GdkEventButton *event, gpointer user_data)
#define DT_IOP_LOWLIGHT_RES
void gui_update(struct dt_iop_module_t *self)
Refresh GUI controls from current params and configuration.
void gui_init(struct dt_iop_module_t *self)
static gboolean lowlight_leave_notify(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)
void input_format(dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece, dt_iop_buffer_dsc_t *dsc)
void gui_cleanup(struct dt_iop_module_t *self)
void init_presets(dt_iop_module_so_t *self)
static void dt_iop_lowlight_get_params(dt_iop_lowlight_params_t *p, const double mouse_x, const double mouse_y, const float rad)
#define DT_IOP_LOWLIGHT_LUT_RES
__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 i, void *const o)
void cleanup_pipe(struct dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
float *const restrict const size_t k
float *const restrict const size_t const size_t ch
float dt_aligned_pixel_t[4]
struct _GtkWidget GtkWidget
struct dt_gui_gtk_t * gui
const struct dt_database_t * db
struct dt_bauhaus_t * bauhaus
struct dt_develop_t * develop
PangoFontDescription * pango_font_desc
struct dt_iop_module_t *void * data
dt_iop_buffer_type_t datatype
GtkWidget * scale_blueness
dt_iop_lowlight_params_t drag_params
dt_draw_curve_t * transition_curve
GModule *dt_dev_operation_t op
dt_iop_params_t * default_params
dt_iop_gui_data_t * gui_data
Region of interest passed through the pixelpipe.