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);
214 ||
g->preview_height !=
height)
218 g->in_preview_buffer = g_malloc_n((
size_t)
width *
height,
sizeof(guchar));
219 g->out_preview_buffer = g_malloc_n((
size_t)
width *
height,
sizeof(guchar));
230 const void *
const ivoid,
void *
const ovoid,
239 const int size =
d->params.size;
247 &&
g->out_preview_buffer)
249 float Lmax[] = { 100.0f };
250 float Lmin[] = { 0.0f };
253 const int radius = 8;
254 const float sigma = 2.5 * (radius * roi_in->
scale);
258 float *tmp = g_malloc_n((
size_t)
width *
height,
sizeof(
float));
306 const int size =
d->params.size;
308 const float *
const restrict in = (
const float *
const)ivoid;
309 float *
const restrict
out = (
float *
const)
ovoid;
310 const size_t npixels = (size_t)roi_out->
width * roi_out->
height;
312 for(
size_t k = 0;
k < (size_t)4 * npixels;
k += 4)
316 const float zs = ((rz > 0) ? (
d->zonemap_offset[rz] / in[
k]) : 0) +
d->zonemap_scale[rz];
319 out[
k+c] = in[
k+c] * zs;
335 d->rzscale = (
d->params.size - 1) / 100.0f;
341 const int size =
d->params.size;
344 for(
int k = 0;
k <
size - 1;
k++)
d->zonemap_scale[
k] = (zonemap[
k + 1] - zonemap[
k]) * (
size - 1);
345 for(
int k = 0;
k <
size - 1;
k++)
346 d->zonemap_offset[
k] = 100.0f * ((
k + 1) * zonemap[
k] -
k * zonemap[
k + 1]);
366 gtk_widget_queue_draw(GTK_WIDGET(
g->zones));
391 if(
g->image) cairo_surface_destroy(
g->image);
398 g->image_buffer = cairo_image_surface_get_data(
g->image);
399 g->image_width = cairo_image_surface_get_width(
g->image);
400 g->image_height = cairo_image_surface_get_height(
g->image);
404 g->image_buffer = NULL;
413 g->in_preview_buffer =
g->out_preview_buffer = NULL;
416 g->preview_width =
g->preview_height = 0;
417 g->mouse_over_output_zones =
FALSE;
424 gtk_widget_add_events(GTK_WIDGET(
g->preview), GDK_POINTER_MOTION_MASK
425 | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK
426 | GDK_LEAVE_NOTIFY_MASK);
429 g->zones = gtk_drawing_area_new();
430 gtk_widget_set_tooltip_text(
g->zones, _(
"lightness zones\nuse mouse scrollwheel to change the number of zones\n"
431 "left-click on a border to create a marker\n"
432 "right-click on a marker to delete it"));
440 g_signal_connect(G_OBJECT(
g->zones),
"button-release-event",
443 gtk_widget_add_events(GTK_WIDGET(
g->zones), GDK_POINTER_MOTION_MASK
444 | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK
457 g->image_buffer = NULL;
469 if(
g->image) cairo_surface_destroy(
g->image);
475#define DT_ZONESYSTEM_INSET DT_PIXEL_APPLY_DPI(5)
476#define DT_ZONESYSTEM_BAR_SPLIT_WIDTH 0.0
477#define DT_ZONESYSTEM_REFERENCE_SPLIT 0.30
484 GtkAllocation allocation;
485 gtk_widget_get_allocation(widget, &allocation);
486 int width = allocation.width,
height = allocation.height;
488 cairo_t *cr = cairo_create(cst);
491 cairo_set_source_rgb(cr, .15, .15, .15);
499 cairo_translate(cr, inset, inset);
505 float s = (1. / (
p->size - 2));
506 cairo_set_antialias(cr, CAIRO_ANTIALIAS_NONE);
507 for(
int i = 0;
i <
p->size - 1;
i++)
511 cairo_rectangle(cr, (1. / (
p->size - 1)) *
i, 0, (1. / (
p->size - 1)),
513 cairo_set_source_rgb(cr, z, z, z);
519 cairo_set_source_rgb(cr, z, z, z);
522 cairo_set_antialias(cr, CAIRO_ANTIALIAS_DEFAULT);
526 cairo_set_antialias(cr, CAIRO_ANTIALIAS_NONE);
527 cairo_set_line_width(cr, 1.);
529 cairo_set_source_rgb(cr, .1, .1, .1);
531 cairo_set_antialias(cr, CAIRO_ANTIALIAS_DEFAULT);
534 cairo_set_source_rgb(cr, 0.6, 0.6, 0.6);
537 for(
int k = 1;
k <
p->size - 1;
k++)
539 float nzw = zonemap[
k + 1] - zonemap[
k];
540 float pzw = zonemap[
k] - zonemap[
k - 1];
541 if((((
g->mouse_x /
width) > (zonemap[
k] - (pzw / 2.0)))
542 && ((
g->mouse_x /
width) < (zonemap[
k] + (nzw / 2.0)))) ||
p->zone[
k] != -1)
544 gboolean is_under_mouse = ((
width * zonemap[
k]) - arrw * .5f <
g->mouse_x
545 && (
width * zonemap[
k]) + arrw * .5f >
g->mouse_x);
547 cairo_move_to(cr, inset + (
width * zonemap[
k]),
height + (2 * inset) - 1);
548 cairo_rel_line_to(cr, -arrw * .5f, 0);
549 cairo_rel_line_to(cr, arrw * .5f, -arrw);
550 cairo_rel_line_to(cr, arrw * .5f, arrw);
551 cairo_close_path(cr);
563 cairo_set_source_surface(crf, cst, 0, 0);
565 cairo_surface_destroy(cst);
576 GtkAllocation allocation;
577 gtk_widget_get_allocation(widget, &allocation);
578 int width = allocation.width - 2 * inset;
586 float zw = zonemap[
k + 1] - zonemap[
k];
587 if((
g->mouse_x /
width) > zonemap[
k] + (zw / 2))
k++;
590 if(event->button == 1)
594 p->zone[
k] = zonemap[
k];
597 g->is_dragging =
TRUE;
600 else if(event->button == 3)
614 if(event->button == 1)
632 gtk_widget_queue_draw(widget);
643 gtk_widget_queue_draw(
g->preview);
653 GtkAllocation allocation;
654 gtk_widget_get_allocation(widget, &allocation);
655 int width = allocation.width - 2 * inset,
height = allocation.height - 2 * inset;
662 g->mouse_x = CLAMP(event->x - inset, 0,
width);
663 g->mouse_y = CLAMP(
height - 1 - event->y + inset, 0,
height);
667 if((
g->mouse_x /
width) > zonemap[
g->current_zone - 1]
668 && (
g->mouse_x /
width) < zonemap[
g->current_zone + 1])
670 p->zone[
g->current_zone] = (
g->mouse_x /
width);
679 g->zone_under_mouse = (
g->mouse_x /
width) / (1.0 / (
p->size - 1));
680 g->mouse_over_output_zones =
TRUE;
684 float xpos =
g->mouse_x /
width;
685 for(
int z = 0; z <
p->size - 1; z++)
687 if(xpos >= zonemap[z] && xpos < zonemap[z + 1])
689 g->zone_under_mouse = z;
693 g->mouse_over_output_zones =
FALSE;
698 gtk_widget_queue_draw(self->
widget);
699 gtk_widget_queue_draw(
g->preview);
707 GtkAllocation allocation;
708 gtk_widget_get_allocation(widget, &allocation);
709 int width = allocation.width,
height = allocation.height;
715 cairo_t *cr = cairo_create(cst);
718 GtkStyleContext *context = gtk_widget_get_style_context(self->
expander);
719 gtk_render_background(context, cr, 0, 0, allocation.width, allocation.height);
723 cairo_translate(cr, inset, inset);
726 if(
g->in_preview_buffer &&
g->out_preview_buffer && self->
enabled)
733 guchar *image = g_malloc_n((
size_t)4 *
g->preview_width *
g->preview_height,
sizeof(guchar));
734 guchar *buffer =
g->mouse_over_output_zones ?
g->out_preview_buffer :
g->in_preview_buffer;
735 for(
int k = 0;
k <
g->preview_width *
g->preview_height;
k++)
737 int zone = 255 *
CLIP(((1.0 / (
p->size - 1)) * buffer[
k]));
738 image[4 *
k + 2] = (
g->hilite_zone && buffer[
k] ==
g->zone_under_mouse) ? 255 : zone;
739 image[4 *
k + 1] = (
g->hilite_zone && buffer[
k] ==
g->zone_under_mouse) ? 255 : zone;
740 image[4 *
k + 0] = (
g->hilite_zone && buffer[
k] ==
g->zone_under_mouse) ? 0 : zone;
744 const int wd =
g->preview_width, ht =
g->preview_height;
745 const float scale = fminf(
width / (
float)wd,
height / (
float)ht);
746 const int stride = cairo_format_stride_for_width(CAIRO_FORMAT_RGB24, wd);
747 cairo_surface_t *surface = cairo_image_surface_create_for_data(image, CAIRO_FORMAT_RGB24, wd, ht, stride);
749 cairo_scale(cr, scale, scale);
750 cairo_translate(cr, -.5f * wd, -.5f * ht);
754 cairo_set_source_surface(cr, surface, 0, 0);
755 cairo_pattern_set_filter(cairo_get_source(cr), CAIRO_FILTER_GOOD);
756 cairo_fill_preserve(cr);
757 cairo_surface_destroy(surface);
760 cairo_set_source_rgb(cr, .1, .1, .1);
772 gtk_style_context_get(context, gtk_widget_get_state_flags(self->
expander),
"background-color", &color,
775 cairo_set_source_surface(cr,
g->image, (
width -
g->image_width) * 0.5, (
height -
g->image_height) * 0.5);
777 cairo_set_operator(cr, CAIRO_OPERATOR_HSL_LUMINOSITY);
778 cairo_fill_preserve(cr);
779 cairo_set_operator(cr, CAIRO_OPERATOR_DARKEN);
780 cairo_set_source_rgb(cr, color->red + 0.02, color->green + 0.02, color->blue + 0.02);
781 cairo_fill_preserve(cr);
782 cairo_set_operator(cr, CAIRO_OPERATOR_LIGHTEN);
783 cairo_set_source_rgb(cr, color->red - 0.02, color->green - 0.02, color->blue - 0.02);
786 gdk_rgba_free(color);
791 cairo_set_source_surface(crf, cst, 0, 0);
793 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