79#include <librsvg/rsvg.h>
83#include <librsvg/rsvg-cairo.h>
88#define MAX_ZONE_SYSTEM_SIZE 24
140 return _(
"zone system");
151 return _(
"this module is deprecated. please use the tone equalizer module instead.");
167 for(
int k = 0;
k <
size - 1;
k++)
168 if(zonemap[
k + 1] >= lightness)
return k;
178 for(
int k = 0;
k <
p->size;
k++)
180 if((
k > 0 && k < p->
size - 1) &&
p->zone[
k] == -1)
185 zonemap[
k] =
k == 0 ? 0.0 :
k == (
p->size - 1) ? 1.0 :
p->zone[
k];
190 for(
int l = 1; l <= steps; l++)
191 zonemap[pk + l] = zonemap[pk] + (((zonemap[
k] - zonemap[pk]) / (steps + 1)) * l);
215 ||
g->preview_height !=
height)
219 g->in_preview_buffer = g_malloc_n((
size_t)
width *
height,
sizeof(guchar));
220 g->out_preview_buffer = g_malloc_n((
size_t)
width *
height,
sizeof(guchar));
231 const void *
const ivoid,
void *
const ovoid,
240 const int size =
d->params.size;
248 &&
g->out_preview_buffer)
250 float Lmax[] = { 100.0f };
251 float Lmin[] = { 0.0f };
254 const int radius = 8;
255 const float sigma = 2.5 * (radius * roi_in->
scale);
259 float *tmp = g_malloc_n((
size_t)
width *
height,
sizeof(
float));
307 const int size =
d->params.size;
309 const float *
const restrict in = (
const float *
const)ivoid;
310 float *
const restrict
out = (
float *
const)
ovoid;
311 const size_t npixels = (size_t)roi_out->
width * roi_out->
height;
313 for(
size_t k = 0;
k < (size_t)4 * npixels;
k += 4)
317 const float zs = ((rz > 0) ? (
d->zonemap_offset[rz] / in[
k]) : 0) +
d->zonemap_scale[rz];
320 out[
k+c] = in[
k+c] * zs;
336 d->rzscale = (
d->params.size - 1) / 100.0f;
342 const int size =
d->params.size;
345 for(
int k = 0;
k <
size - 1;
k++)
d->zonemap_scale[
k] = (zonemap[
k + 1] - zonemap[
k]) * (
size - 1);
346 for(
int k = 0;
k <
size - 1;
k++)
347 d->zonemap_offset[
k] = 100.0f * ((
k + 1) * zonemap[
k] -
k * zonemap[
k + 1]);
367 gtk_widget_queue_draw(GTK_WIDGET(
g->zones));
392 if(
g->image) cairo_surface_destroy(
g->image);
399 g->image_buffer = cairo_image_surface_get_data(
g->image);
400 g->image_width = cairo_image_surface_get_width(
g->image);
401 g->image_height = cairo_image_surface_get_height(
g->image);
405 g->image_buffer = NULL;
414 g->in_preview_buffer =
g->out_preview_buffer = NULL;
417 g->preview_width =
g->preview_height = 0;
418 g->mouse_over_output_zones =
FALSE;
425 gtk_widget_add_events(GTK_WIDGET(
g->preview), GDK_POINTER_MOTION_MASK
426 | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK
427 | GDK_LEAVE_NOTIFY_MASK);
430 g->zones = gtk_drawing_area_new();
431 gtk_widget_set_tooltip_text(
g->zones, _(
"lightness zones\nuse mouse scrollwheel to change the number of zones\n"
432 "left-click on a border to create a marker\n"
433 "right-click on a marker to delete it"));
441 g_signal_connect(G_OBJECT(
g->zones),
"button-release-event",
444 gtk_widget_add_events(GTK_WIDGET(
g->zones), GDK_POINTER_MOTION_MASK
445 | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK
458 g->image_buffer = NULL;
470 if(
g->image) cairo_surface_destroy(
g->image);
476#define DT_ZONESYSTEM_INSET DT_PIXEL_APPLY_DPI(5)
477#define DT_ZONESYSTEM_BAR_SPLIT_WIDTH 0.0
478#define DT_ZONESYSTEM_REFERENCE_SPLIT 0.30
485 GtkAllocation allocation;
486 gtk_widget_get_allocation(widget, &allocation);
487 int width = allocation.width,
height = allocation.height;
489 cairo_t *cr = cairo_create(cst);
492 cairo_set_source_rgb(cr, .15, .15, .15);
500 cairo_translate(cr, inset, inset);
506 float s = (1. / (
p->size - 2));
507 cairo_set_antialias(cr, CAIRO_ANTIALIAS_NONE);
508 for(
int i = 0;
i <
p->size - 1;
i++)
512 cairo_rectangle(cr, (1. / (
p->size - 1)) *
i, 0, (1. / (
p->size - 1)),
514 cairo_set_source_rgb(cr, z, z, z);
520 cairo_set_source_rgb(cr, z, z, z);
523 cairo_set_antialias(cr, CAIRO_ANTIALIAS_DEFAULT);
527 cairo_set_antialias(cr, CAIRO_ANTIALIAS_NONE);
528 cairo_set_line_width(cr, 1.);
530 cairo_set_source_rgb(cr, .1, .1, .1);
532 cairo_set_antialias(cr, CAIRO_ANTIALIAS_DEFAULT);
535 cairo_set_source_rgb(cr, 0.6, 0.6, 0.6);
538 for(
int k = 1;
k <
p->size - 1;
k++)
540 float nzw = zonemap[
k + 1] - zonemap[
k];
541 float pzw = zonemap[
k] - zonemap[
k - 1];
542 if((((
g->mouse_x /
width) > (zonemap[
k] - (pzw / 2.0)))
543 && ((
g->mouse_x /
width) < (zonemap[
k] + (nzw / 2.0)))) ||
p->zone[
k] != -1)
545 gboolean is_under_mouse = ((
width * zonemap[
k]) - arrw * .5f <
g->mouse_x
546 && (
width * zonemap[
k]) + arrw * .5f >
g->mouse_x);
548 cairo_move_to(cr, inset + (
width * zonemap[
k]),
height + (2 * inset) - 1);
549 cairo_rel_line_to(cr, -arrw * .5f, 0);
550 cairo_rel_line_to(cr, arrw * .5f, -arrw);
551 cairo_rel_line_to(cr, arrw * .5f, arrw);
552 cairo_close_path(cr);
564 cairo_set_source_surface(crf, cst, 0, 0);
566 cairo_surface_destroy(cst);
577 GtkAllocation allocation;
578 gtk_widget_get_allocation(widget, &allocation);
579 int width = allocation.width - 2 * inset;
587 float zw = zonemap[
k + 1] - zonemap[
k];
588 if((
g->mouse_x /
width) > zonemap[
k] + (zw / 2))
k++;
591 if(event->button == 1)
595 p->zone[
k] = zonemap[
k];
598 g->is_dragging =
TRUE;
601 else if(event->button == 3)
615 if(event->button == 1)
633 gtk_widget_queue_draw(widget);
644 gtk_widget_queue_draw(
g->preview);
654 GtkAllocation allocation;
655 gtk_widget_get_allocation(widget, &allocation);
656 int width = allocation.width - 2 * inset,
height = allocation.height - 2 * inset;
663 g->mouse_x = CLAMP(event->x - inset, 0,
width);
664 g->mouse_y = CLAMP(
height - 1 - event->y + inset, 0,
height);
668 if((
g->mouse_x /
width) > zonemap[
g->current_zone - 1]
669 && (
g->mouse_x /
width) < zonemap[
g->current_zone + 1])
671 p->zone[
g->current_zone] = (
g->mouse_x /
width);
680 g->zone_under_mouse = (
g->mouse_x /
width) / (1.0 / (
p->size - 1));
681 g->mouse_over_output_zones =
TRUE;
685 float xpos =
g->mouse_x /
width;
686 for(
int z = 0; z <
p->size - 1; z++)
688 if(xpos >= zonemap[z] && xpos < zonemap[z + 1])
690 g->zone_under_mouse = z;
694 g->mouse_over_output_zones =
FALSE;
699 gtk_widget_queue_draw(self->
gui->
widget);
700 gtk_widget_queue_draw(
g->preview);
708 GtkAllocation allocation;
709 gtk_widget_get_allocation(widget, &allocation);
710 int width = allocation.width,
height = allocation.height;
716 cairo_t *cr = cairo_create(cst);
719 GtkStyleContext *context = gtk_widget_get_style_context(self->
gui->
expander);
720 gtk_render_background(context, cr, 0, 0, allocation.width, allocation.height);
724 cairo_translate(cr, inset, inset);
727 if(
g->in_preview_buffer &&
g->out_preview_buffer && self->
enabled)
734 guchar *image = g_malloc_n((
size_t)4 *
g->preview_width *
g->preview_height,
sizeof(guchar));
735 guchar *buffer =
g->mouse_over_output_zones ?
g->out_preview_buffer :
g->in_preview_buffer;
736 for(
int k = 0;
k <
g->preview_width *
g->preview_height;
k++)
738 int zone = 255 *
CLIP(((1.0 / (
p->size - 1)) * buffer[
k]));
739 image[4 *
k + 2] = (
g->hilite_zone && buffer[
k] ==
g->zone_under_mouse) ? 255 : zone;
740 image[4 *
k + 1] = (
g->hilite_zone && buffer[
k] ==
g->zone_under_mouse) ? 255 : zone;
741 image[4 *
k + 0] = (
g->hilite_zone && buffer[
k] ==
g->zone_under_mouse) ? 0 : zone;
745 const int wd =
g->preview_width, ht =
g->preview_height;
746 const float scale = fminf(
width / (
float)wd,
height / (
float)ht);
747 const int stride = cairo_format_stride_for_width(CAIRO_FORMAT_RGB24, wd);
748 cairo_surface_t *surface = cairo_image_surface_create_for_data(image, CAIRO_FORMAT_RGB24, wd, ht, stride);
750 cairo_scale(cr, scale, scale);
751 cairo_translate(cr, -.5f * wd, -.5f * ht);
755 cairo_set_source_surface(cr, surface, 0, 0);
756 cairo_pattern_set_filter(cairo_get_source(cr), CAIRO_FILTER_GOOD);
757 cairo_fill_preserve(cr);
758 cairo_surface_destroy(surface);
761 cairo_set_source_rgb(cr, .1, .1, .1);
773 gtk_style_context_get(context, gtk_widget_get_state_flags(self->
gui->
expander),
"background-color", &
color,
776 cairo_set_source_surface(cr,
g->image, (
width -
g->image_width) * 0.5, (
height -
g->image_height) * 0.5);
778 cairo_set_operator(cr, CAIRO_OPERATOR_HSL_LUMINOSITY);
779 cairo_fill_preserve(cr);
780 cairo_set_operator(cr, CAIRO_OPERATOR_DARKEN);
781 cairo_set_source_rgb(cr,
color->red + 0.02,
color->green + 0.02,
color->blue + 0.02);
782 cairo_fill_preserve(cr);
783 cairo_set_operator(cr, CAIRO_OPERATOR_LIGHTEN);
784 cairo_set_source_rgb(cr,
color->red - 0.02,
color->green - 0.02,
color->blue - 0.02);
787 gdk_rgba_free(
color);
792 cairo_set_source_surface(crf, cst, 0, 0);
794 cairo_surface_destroy(cst);
struct _GtkWidget GtkWidget
GtkWidget, opaque, spelled exactly as GTK spells it.
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...
#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)
@ DT_DEV_PIXELPIPE_DISPLAY_MASK
GtkWidget * dtgtk_drawing_area_new_with_aspect_ratio(double aspect)
void dt_gaussian_free(dt_gaussian_t *g)
__DT_CLONE_TARGETS__ void dt_gaussian_blur(dt_gaussian_t *g, const float *const in, float *const out)
dt_gaussian_t * dt_gaussian_init(const int width, const int height, const int channels, const float *max, const float *min, const float sigma, const int order)
void dt_iop_gui_leave_critical_section(dt_iop_module_t *const module)
Release what dt_iop_gui_enter_critical_section() took. Also a no-op headless.
@ IOP_FLAGS_INCLUDE_IN_STYLES
@ IOP_FLAGS_SUPPORTS_BLENDING
void dt_iop_gui_enter_critical_section(dt_iop_module_t *const module)
Take the module's GUI lock, serialising access to its dt_iop_gui_data_t.
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)
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_free(ptr)
g_free() ptr and set it to NULL, skipping both if it is already NULL.
#define DT_MODULE_INTROSPECTION(MODVER, PARAMSTYPE)
DT_MODULE() for a module whose params struct is introspected.
#define __OMP_PARALLEL_FOR__(...)
static __DT_CLONE_TARGETS__ void process_common_setup(dt_iop_module_t *self, const dt_dev_pixelpipe_iop_t *piece)
static cairo_surface_t * dt_cairo_image_surface_create(cairo_format_t format, int width, int height)
#define DT_DEBUG_CONTROL_SIGNAL_DISCONNECT(ctlsig, cb, user_data)
struct dt_control_signal_t * dt_control_signal_get_global(void)
@ DT_SIGNAL_DEVELOP_PREVIEW_PIPE_FINISHED
This signal is raised when develop preview pipe process is finished no param, no returned value.
#define DT_DEBUG_CONTROL_SIGNAL_CONNECT(ctlsig, signal, cb, user_data)
#define for_each_channel(_var,...)
float dt_aligned_pixel_simd_t __attribute__((vector_size(16), aligned(16)))
Apply one channel's tone curve to each of the three colour channels, or pass the channel through unto...
dt_iop_buffer_dsc_t dsc_in
struct dt_iop_module_t *void * data
struct dt_iop_module_gui_t * gui
struct dt_develop_t * dev
Region of interest passed through the pixelpipe.
dt_iop_zonesystem_params_t params
guchar * in_preview_buffer
int mouse_over_output_zones
guchar * out_preview_buffer
#define __DT_CLONE_TARGETS__
cairo_surface_t * dt_util_get_logo(const float size)
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)
static gboolean dt_iop_zonesystem_bar_leave_notify(GtkWidget *widget, GdkEventCrossing *event, 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)
#define DT_ZONESYSTEM_BAR_SPLIT_WIDTH
static gboolean dt_iop_zonesystem_bar_button_press(GtkWidget *widget, GdkEventButton *event, dt_iop_module_t *self)
static gboolean dt_iop_zonesystem_bar_draw(GtkWidget *widget, cairo_t *crf, dt_iop_module_t *self)
#define DT_ZONESYSTEM_REFERENCE_SPLIT
static gboolean dt_iop_zonesystem_preview_draw(GtkWidget *widget, cairo_t *crf, dt_iop_module_t *self)
void init_pipe(struct dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
void gui_update(struct dt_iop_module_t *self)
Refresh GUI controls from current params and configuration.
static gboolean dt_iop_zonesystem_bar_scrolled(GtkWidget *widget, GdkEventScroll *event, dt_iop_module_t *self)
static void _iop_zonesystem_calculate_zonemap(struct dt_iop_zonesystem_params_t *p, float *zonemap)
static int _iop_zonesystem_zone_index_from_lightness(float lightness, float *zonemap, int size)
void gui_init(struct dt_iop_module_t *self)
static void _iop_zonesystem_redraw_preview_callback(gpointer instance, gpointer user_data)
#define DT_ZONESYSTEM_INSET
static void size_allocate_callback(GtkWidget *widget, GtkAllocation *allocation, gpointer user_data)
int default_colorspace(dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece)
void gui_cleanup(struct dt_iop_module_t *self)
static gboolean dt_iop_zonesystem_bar_button_release(GtkWidget *widget, GdkEventButton *event, dt_iop_module_t *self)
void cleanup_pipe(struct dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
static __DT_CLONE_TARGETS__ void process_common_cleanup(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, const dt_iop_roi_t *const roi_in, const dt_iop_roi_t *const roi_out)
const char * deprecated_msg()
static gboolean dt_iop_zonesystem_bar_motion_notify(GtkWidget *widget, GdkEventMotion *event, dt_iop_module_t *self)
#define MAX_ZONE_SYSTEM_SIZE