75#include <librsvg/rsvg.h>
78#include <librsvg/rsvg-cairo.h>
83#define MAX_ZONE_SYSTEM_SIZE 24
139 return _(
"zone system");
150 return _(
"this module is deprecated. please use the tone equalizer module instead.");
166 for(
int k = 0;
k <
size - 1;
k++)
167 if(zonemap[
k + 1] >= lightness)
return k;
177 for(
int k = 0;
k <
p->size;
k++)
179 if((
k > 0 && k < p->
size - 1) &&
p->zone[
k] == -1)
184 zonemap[
k] =
k == 0 ? 0.0 :
k == (
p->size - 1) ? 1.0 :
p->zone[
k];
189 for(
int l = 1; l <= steps; l++)
190 zonemap[pk + l] = zonemap[pk] + (((zonemap[
k] - zonemap[pk]) / (steps + 1)) * l);
212 ||
g->preview_height !=
height)
216 g->in_preview_buffer = g_malloc_n((
size_t)
width *
height,
sizeof(guchar));
217 g->out_preview_buffer = g_malloc_n((
size_t)
width *
height,
sizeof(guchar));
228 const void *
const ivoid,
void *
const ovoid,
237 const int size =
d->params.size;
245 &&
g->out_preview_buffer)
247 float Lmax[] = { 100.0f };
248 float Lmin[] = { 0.0f };
251 const int radius = 8;
252 const float sigma = 2.5 * (radius * roi_in->
scale);
256 float *tmp = g_malloc_n((
size_t)
width *
height,
sizeof(
float));
304 const int size =
d->params.size;
306 const float *
const restrict in = (
const float *
const)ivoid;
307 float *
const restrict
out = (
float *
const)
ovoid;
308 const size_t npixels = (size_t)roi_out->
width * roi_out->
height;
310 for(
size_t k = 0;
k < (size_t)4 * npixels;
k += 4)
314 const float zs = ((rz > 0) ? (
d->zonemap_offset[rz] / in[
k]) : 0) +
d->zonemap_scale[rz];
317 out[
k+c] = in[
k+c] * zs;
333 d->rzscale = (
d->params.size - 1) / 100.0f;
339 const int size =
d->params.size;
342 for(
int k = 0;
k <
size - 1;
k++)
d->zonemap_scale[
k] = (zonemap[
k + 1] - zonemap[
k]) * (
size - 1);
343 for(
int k = 0;
k <
size - 1;
k++)
344 d->zonemap_offset[
k] = 100.0f * ((
k + 1) * zonemap[
k] -
k * zonemap[
k + 1]);
364 gtk_widget_queue_draw(GTK_WIDGET(
g->zones));
389 if(
g->image) cairo_surface_destroy(
g->image);
396 g->image_buffer = cairo_image_surface_get_data(
g->image);
397 g->image_width = cairo_image_surface_get_width(
g->image);
398 g->image_height = cairo_image_surface_get_height(
g->image);
402 g->image_buffer = NULL;
411 g->in_preview_buffer =
g->out_preview_buffer = NULL;
414 g->preview_width =
g->preview_height = 0;
415 g->mouse_over_output_zones =
FALSE;
422 gtk_widget_add_events(GTK_WIDGET(
g->preview), GDK_POINTER_MOTION_MASK
423 | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK
424 | GDK_LEAVE_NOTIFY_MASK);
427 g->zones = gtk_drawing_area_new();
428 gtk_widget_set_tooltip_text(
g->zones, _(
"lightness zones\nuse mouse scrollwheel to change the number of zones\n"
429 "left-click on a border to create a marker\n"
430 "right-click on a marker to delete it"));
438 g_signal_connect(G_OBJECT(
g->zones),
"button-release-event",
441 gtk_widget_add_events(GTK_WIDGET(
g->zones), GDK_POINTER_MOTION_MASK
442 | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK
455 g->image_buffer = NULL;
467 if(
g->image) cairo_surface_destroy(
g->image);
473#define DT_ZONESYSTEM_INSET DT_PIXEL_APPLY_DPI(5)
474#define DT_ZONESYSTEM_BAR_SPLIT_WIDTH 0.0
475#define DT_ZONESYSTEM_REFERENCE_SPLIT 0.30
482 GtkAllocation allocation;
483 gtk_widget_get_allocation(widget, &allocation);
484 int width = allocation.width,
height = allocation.height;
486 cairo_t *cr = cairo_create(cst);
489 cairo_set_source_rgb(cr, .15, .15, .15);
497 cairo_translate(cr, inset, inset);
503 float s = (1. / (
p->size - 2));
504 cairo_set_antialias(cr, CAIRO_ANTIALIAS_NONE);
505 for(
int i = 0;
i <
p->size - 1;
i++)
509 cairo_rectangle(cr, (1. / (
p->size - 1)) *
i, 0, (1. / (
p->size - 1)),
511 cairo_set_source_rgb(cr, z, z, z);
517 cairo_set_source_rgb(cr, z, z, z);
520 cairo_set_antialias(cr, CAIRO_ANTIALIAS_DEFAULT);
524 cairo_set_antialias(cr, CAIRO_ANTIALIAS_NONE);
525 cairo_set_line_width(cr, 1.);
527 cairo_set_source_rgb(cr, .1, .1, .1);
529 cairo_set_antialias(cr, CAIRO_ANTIALIAS_DEFAULT);
532 cairo_set_source_rgb(cr, 0.6, 0.6, 0.6);
535 for(
int k = 1;
k <
p->size - 1;
k++)
537 float nzw = zonemap[
k + 1] - zonemap[
k];
538 float pzw = zonemap[
k] - zonemap[
k - 1];
539 if((((
g->mouse_x /
width) > (zonemap[
k] - (pzw / 2.0)))
540 && ((
g->mouse_x /
width) < (zonemap[
k] + (nzw / 2.0)))) ||
p->zone[
k] != -1)
542 gboolean is_under_mouse = ((
width * zonemap[
k]) - arrw * .5f <
g->mouse_x
543 && (
width * zonemap[
k]) + arrw * .5f >
g->mouse_x);
545 cairo_move_to(cr, inset + (
width * zonemap[
k]),
height + (2 * inset) - 1);
546 cairo_rel_line_to(cr, -arrw * .5f, 0);
547 cairo_rel_line_to(cr, arrw * .5f, -arrw);
548 cairo_rel_line_to(cr, arrw * .5f, arrw);
549 cairo_close_path(cr);
561 cairo_set_source_surface(crf, cst, 0, 0);
563 cairo_surface_destroy(cst);
574 GtkAllocation allocation;
575 gtk_widget_get_allocation(widget, &allocation);
576 int width = allocation.width - 2 * inset;
584 float zw = zonemap[
k + 1] - zonemap[
k];
585 if((
g->mouse_x /
width) > zonemap[
k] + (zw / 2))
k++;
588 if(event->button == 1)
592 p->zone[
k] = zonemap[
k];
595 g->is_dragging =
TRUE;
598 else if(event->button == 3)
612 if(event->button == 1)
630 gtk_widget_queue_draw(widget);
641 gtk_widget_queue_draw(
g->preview);
651 GtkAllocation allocation;
652 gtk_widget_get_allocation(widget, &allocation);
653 int width = allocation.width - 2 * inset,
height = allocation.height - 2 * inset;
660 g->mouse_x = CLAMP(event->x - inset, 0,
width);
661 g->mouse_y = CLAMP(
height - 1 - event->y + inset, 0,
height);
665 if((
g->mouse_x /
width) > zonemap[
g->current_zone - 1]
666 && (
g->mouse_x /
width) < zonemap[
g->current_zone + 1])
668 p->zone[
g->current_zone] = (
g->mouse_x /
width);
677 g->zone_under_mouse = (
g->mouse_x /
width) / (1.0 / (
p->size - 1));
678 g->mouse_over_output_zones =
TRUE;
682 float xpos =
g->mouse_x /
width;
683 for(
int z = 0; z <
p->size - 1; z++)
685 if(xpos >= zonemap[z] && xpos < zonemap[z + 1])
687 g->zone_under_mouse = z;
691 g->mouse_over_output_zones =
FALSE;
696 gtk_widget_queue_draw(self->
widget);
697 gtk_widget_queue_draw(
g->preview);
705 GtkAllocation allocation;
706 gtk_widget_get_allocation(widget, &allocation);
707 int width = allocation.width,
height = allocation.height;
713 cairo_t *cr = cairo_create(cst);
716 GtkStyleContext *context = gtk_widget_get_style_context(self->
expander);
717 gtk_render_background(context, cr, 0, 0, allocation.width, allocation.height);
721 cairo_translate(cr, inset, inset);
724 if(
g->in_preview_buffer &&
g->out_preview_buffer && self->
enabled)
731 guchar *image = g_malloc_n((
size_t)4 *
g->preview_width *
g->preview_height,
sizeof(guchar));
732 guchar *buffer =
g->mouse_over_output_zones ?
g->out_preview_buffer :
g->in_preview_buffer;
733 for(
int k = 0;
k <
g->preview_width *
g->preview_height;
k++)
735 int zone = 255 *
CLIP(((1.0 / (
p->size - 1)) * buffer[
k]));
736 image[4 *
k + 2] = (
g->hilite_zone && buffer[
k] ==
g->zone_under_mouse) ? 255 : zone;
737 image[4 *
k + 1] = (
g->hilite_zone && buffer[
k] ==
g->zone_under_mouse) ? 255 : zone;
738 image[4 *
k + 0] = (
g->hilite_zone && buffer[
k] ==
g->zone_under_mouse) ? 0 : zone;
742 const int wd =
g->preview_width, ht =
g->preview_height;
743 const float scale = fminf(
width / (
float)wd,
height / (
float)ht);
744 const int stride = cairo_format_stride_for_width(CAIRO_FORMAT_RGB24, wd);
745 cairo_surface_t *surface = cairo_image_surface_create_for_data(image, CAIRO_FORMAT_RGB24, wd, ht, stride);
747 cairo_scale(cr, scale, scale);
748 cairo_translate(cr, -.5f * wd, -.5f * ht);
752 cairo_set_source_surface(cr, surface, 0, 0);
753 cairo_pattern_set_filter(cairo_get_source(cr), CAIRO_FILTER_GOOD);
754 cairo_fill_preserve(cr);
755 cairo_surface_destroy(surface);
758 cairo_set_source_rgb(cr, .1, .1, .1);
770 gtk_style_context_get(context, gtk_widget_get_state_flags(self->
expander),
"background-color", &color,
773 cairo_set_source_surface(cr,
g->image, (
width -
g->image_width) * 0.5, (
height -
g->image_height) * 0.5);
775 cairo_set_operator(cr, CAIRO_OPERATOR_HSL_LUMINOSITY);
776 cairo_fill_preserve(cr);
777 cairo_set_operator(cr, CAIRO_OPERATOR_DARKEN);
778 cairo_set_source_rgb(cr, color->red + 0.02, color->green + 0.02, color->blue + 0.02);
779 cairo_fill_preserve(cr);
780 cairo_set_operator(cr, CAIRO_OPERATOR_LIGHTEN);
781 cairo_set_source_rgb(cr, color->red - 0.02, color->green - 0.02, color->blue - 0.02);
784 gdk_rgba_free(color);
789 cairo_set_source_surface(crf, cst, 0, 0);
791 cairo_surface_destroy(cst);
static const dt_aligned_pixel_simd_t const dt_adaptation_t const float p
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_free_align(ptr)
static void * dt_calloc_align(size_t size)
#define for_each_channel(_var,...)
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 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_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)
gboolean dt_gui_get_scroll_unit_deltas(const GdkEventScroll *event, int *delta_x, int *delta_y)
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)
static void dt_iop_gui_enter_critical_section(dt_iop_module_t *const module) ACQUIRE(&module -> gui_lock)
@ IOP_FLAGS_INCLUDE_IN_STYLES
@ IOP_FLAGS_SUPPORTS_BLENDING
static void dt_iop_gui_leave_critical_section(dt_iop_module_t *const module) RELEASE(&module -> gui_lock)
#define IOP_GUI_ALLOC(module)
float *const restrict const size_t k
float *const restrict const size_t const size_t ch
static __DT_CLONE_TARGETS__ void process_common_setup(dt_iop_module_t *self, const dt_dev_pixelpipe_iop_t *piece)
#define DT_DEBUG_CONTROL_SIGNAL_DISCONNECT(ctlsig, cb, user_data)
@ 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)
struct _GtkWidget GtkWidget
struct dt_gui_gtk_t * gui
struct dt_control_signal_t * signals
struct dt_develop_t * develop
dt_iop_buffer_dsc_t dsc_in
struct dt_iop_module_t *void * data
struct dt_develop_t * dev
dt_iop_gui_data_t * gui_data
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
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