Ansel 0.0
A darktable fork - bloat + design vision
Loading...
Searching...
No Matches
zonesystem.c
Go to the documentation of this file.
1/*
2 This file is part of darktable,
3 Copyright (C) 2010-2011 Bruce Guenter.
4 Copyright (C) 2010-2012 Henrik Andersson.
5 Copyright (C) 2010-2013, 2016 johannes hanika.
6 Copyright (C) 2010 Pascal de Bruijn.
7 Copyright (C) 2011 Antony Dovgal.
8 Copyright (C) 2011 Brian Teague.
9 Copyright (C) 2011 Olivier Tribout.
10 Copyright (C) 2011 Robert Bieber.
11 Copyright (C) 2011 Rostyslav Pidgornyi.
12 Copyright (C) 2011-2014, 2016-2017, 2019 Tobias Ellinghaus.
13 Copyright (C) 2012 José Carlos García Sogo.
14 Copyright (C) 2012 Loic Guibert.
15 Copyright (C) 2012 Richard Wonka.
16 Copyright (C) 2012-2014, 2016-2017 Ulrich Pegelow.
17 Copyright (C) 2013-2016 Roman Lebedev.
18 Copyright (C) 2014 parafin.
19 Copyright (C) 2015 Pedro Côrte-Real.
20 Copyright (C) 2017-2018, 2021 Dan Torop.
21 Copyright (C) 2017 Heiko Bauke.
22 Copyright (C) 2018, 2020, 2022-2023, 2025-2026 Aurélien PIERRE.
23 Copyright (C) 2018 Edgardo Hoszowski.
24 Copyright (C) 2018 Maurizio Paglia.
25 Copyright (C) 2018-2021 Pascal Obry.
26 Copyright (C) 2018 rawfiner.
27 Copyright (C) 2019 Andreas Schneider.
28 Copyright (C) 2019 emeikei.
29 Copyright (C) 2019 luzpaz.
30 Copyright (C) 2020 Aldric Renaudin.
31 Copyright (C) 2020 Diederik Ter Rahe.
32 Copyright (C) 2020-2021 Hubert Kowalski.
33 Copyright (C) 2020-2021 Ralf Brown.
34 Copyright (C) 2021 Chris Elston.
35 Copyright (C) 2022 Hanno Schwalm.
36 Copyright (C) 2022 Martin Bařinka.
37 Copyright (C) 2022 Philipp Lutz.
38
39 darktable is free software: you can redistribute it and/or modify
40 it under the terms of the GNU General Public License as published by
41 the Free Software Foundation, either version 3 of the License, or
42 (at your option) any later version.
43
44 darktable is distributed in the hope that it will be useful,
45 but WITHOUT ANY WARRANTY; without even the implied warranty of
46 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
47 GNU General Public License for more details.
48
49 You should have received a copy of the GNU General Public License
50 along with darktable. If not, see <http://www.gnu.org/licenses/>.
51*/
52#ifdef HAVE_CONFIG_H
53#include "config.h"
54#endif
55#include <assert.h>
56#include <stdlib.h>
57#include <string.h>
58
59#include "common/darktable.h"
60#include "common/gaussian.h"
61#include "common/math.h"
62#include "common/opencl.h"
63#include "control/conf.h"
64#include "control/control.h"
65#include "develop/develop.h"
66#include "develop/imageop.h"
68#include "dtgtk/drawingarea.h"
70#include "dtgtk/togglebutton.h"
71#include "gui/gtk.h"
72#include "gui/presets.h"
73#include "iop/iop_api.h"
74
75#include <librsvg/rsvg.h>
76// ugh, ugly hack. why do people break stuff all the time?
77#ifndef RSVG_CAIRO_H
78#include <librsvg/rsvg-cairo.h>
79#endif
80
81
83#define MAX_ZONE_SYSTEM_SIZE 24
84
87{
88 int size; // $DEFAULT: 10
89 float zone[MAX_ZONE_SYSTEM_SIZE + 1]; // $DEFAULT: -1.0
91
100
101
102/*
103void init_presets (dt_iop_module_so_t *self)
104{
105// DT_DEBUG_SQLITE3_EXEC(darktable.db, "begin", NULL, NULL, NULL);
106
107 dt_gui_presets_add_generic(_("Fill-light 0.25EV with 4 zones"), self->op, self->version(),
108&(dt_iop_zonesystem_params_t){0.25,0.25,4.0} , sizeof(dt_iop_zonesystem_params_t), 1);
109 dt_gui_presets_add_generic(_("Fill-shadow -0.25EV with 4 zones"), self->op, self->version(),
110&(dt_iop_zonesystem_params_t){-0.25,0.25,4.0} , sizeof(dt_iop_zonesystem_params_t), 1);
111
112// DT_DEBUG_SQLITE3_EXEC(darktable.db, "commit", NULL, NULL, NULL);
113}
114*/
115
135
136
137const char *name()
138{
139 return _("zone system");
140}
141
147
148const char *deprecated_msg()
149{
150 return _("this module is deprecated. please use the tone equalizer module instead.");
151}
152
154{
155 return IOP_GROUP_TONES;
156}
157
159{
160 return IOP_CS_LAB;
161}
162
163/* get the zone index of pixel lightness from zonemap */
164static inline int _iop_zonesystem_zone_index_from_lightness(float lightness, float *zonemap, int size)
165{
166 for(int k = 0; k < size - 1; k++)
167 if(zonemap[k + 1] >= lightness) return k;
168 return size - 1;
169}
170
171/* calculate a zonemap with scale values for each zone based on controlpoints from param */
172static inline void _iop_zonesystem_calculate_zonemap(struct dt_iop_zonesystem_params_t *p, float *zonemap)
173{
174 int steps = 0;
175 int pk = 0;
176
177 for(int k = 0; k < p->size; k++)
178 {
179 if((k > 0 && k < p->size - 1) && p->zone[k] == -1)
180 steps++;
181 else
182 {
183 /* set 0 and 1.0 for first and last element in zonesystem size, or the actually parameter value */
184 zonemap[k] = k == 0 ? 0.0 : k == (p->size - 1) ? 1.0 : p->zone[k];
185
186 /* for each step from pk to k, calculate values
187 for now this is linear distributed
188 */
189 for(int l = 1; l <= steps; l++)
190 zonemap[pk + l] = zonemap[pk] + (((zonemap[k] - zonemap[pk]) / (steps + 1)) * l);
191
192 /* store k into pk and reset zone steps for next range*/
193 pk = k;
194 steps = 0;
195 }
196 }
197}
198
199static inline __attribute__((always_inline)) void process_common_setup(struct dt_iop_module_t *self, const dt_dev_pixelpipe_t *pipe,
200 const dt_dev_pixelpipe_iop_t *piece,
201 const void *const ivoid, void *const ovoid, const dt_iop_roi_t *const roi_in,
202 const dt_iop_roi_t *const roi_out)
203{
204 const int width = roi_out->width;
205 const int height = roi_out->height;
206
207 if(self->dev->gui_attached && dt_dev_pixelpipe_has_preview_output(self->dev, pipe, roi_out))
208 {
211 if(IS_NULL_PTR(g->in_preview_buffer) || IS_NULL_PTR(g->out_preview_buffer) || g->preview_width != width
212 || g->preview_height != height)
213 {
214 dt_free(g->in_preview_buffer);
215 dt_free(g->out_preview_buffer);
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));
218 g->preview_width = width;
219 g->preview_height = height;
220 }
222 }
223}
224
226static void process_common_cleanup(struct dt_iop_module_t *self, const dt_dev_pixelpipe_t *pipe,
227 const dt_dev_pixelpipe_iop_t *piece,
228 const void *const ivoid, void *const ovoid,
229 const dt_iop_roi_t *const roi_in, const dt_iop_roi_t *const roi_out)
230{
233
234 const int width = roi_out->width;
235 const int height = roi_out->height;
236 const size_t ch = piece->dsc_in.channels;
237 const int size = d->params.size;
238
239 if(pipe->mask_display & DT_DEV_PIXELPIPE_DISPLAY_MASK) dt_iop_alpha_copy(ivoid, ovoid, width, height);
240
241 /* if gui and have buffer lets gaussblur and fill buffer with zone indexes */
242 if(self->dev->gui_attached
243 && dt_dev_pixelpipe_has_preview_output(self->dev, pipe, roi_out)
244 && !IS_NULL_PTR(g) && g->in_preview_buffer
245 && g->out_preview_buffer)
246 {
247 float Lmax[] = { 100.0f };
248 float Lmin[] = { 0.0f };
249
250 /* setup gaussian kernel */
251 const int radius = 8;
252 const float sigma = 2.5 * (radius * roi_in->scale);
253
255
256 float *tmp = g_malloc_n((size_t)width * height, sizeof(float));
257
258 if(gauss && !IS_NULL_PTR(tmp))
259 {
261 for(size_t k = 0; k < (size_t)width * height; k++) tmp[k] = ((float *)ivoid)[ch * k];
262
263 dt_gaussian_blur(gauss, tmp, tmp);
264
265 /* create zonemap preview for input */
268 for(size_t k = 0; k < (size_t)width * height; k++)
269 {
270 g->in_preview_buffer[k] = CLAMPS(tmp[k] * (size - 1) / 100.0f, 0, size - 2);
271 }
274 for(size_t k = 0; k < (size_t)width * height; k++) tmp[k] = ((float *)ovoid)[ch * k];
275
276 dt_gaussian_blur(gauss, tmp, tmp);
277
278
279 /* create zonemap preview for output */
282 for(size_t k = 0; k < (size_t)width * height; k++)
283 {
284 g->out_preview_buffer[k] = CLAMPS(tmp[k] * (size - 1) / 100.0f, 0, size - 2);
285 }
287 }
288
289 dt_free(tmp);
290 if(gauss) dt_gaussian_free(gauss);
291 }
292}
293
295int process(struct dt_iop_module_t *self, const dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece, const void *const ivoid,
296 void *const ovoid)
297{
298 const dt_iop_roi_t *const roi_in = &piece->roi_in;
299 const dt_iop_roi_t *const roi_out = &piece->roi_out;
300
301 const dt_iop_zonesystem_data_t *const d = (const dt_iop_zonesystem_data_t *const)piece->data;
302 process_common_setup(self, pipe, piece, ivoid, ovoid, roi_in, roi_out);
303
304 const int size = d->params.size;
305
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)
311 {
312 /* remap lightness into zonemap and apply lightness */
313 const int rz = CLAMPS(in[k] * d->rzscale, 0, size - 2); // zone index
314 const float zs = ((rz > 0) ? (d->zonemap_offset[rz] / in[k]) : 0) + d->zonemap_scale[rz];
315 for_each_channel(c,aligned(in,out))
316 {
317 out[k+c] = in[k+c] * zs;
318 }
319 }
320
321 process_common_cleanup(self, pipe, piece, ivoid, ovoid, roi_in, roi_out);
322 return 0;
323}
324
327{
329
331
332 d->params = *p;
333 d->rzscale = (d->params.size - 1) / 100.0f;
334
335 /* calculate zonemap */
336 float zonemap[MAX_ZONE_SYSTEM_SIZE] = { -1 };
337 _iop_zonesystem_calculate_zonemap(&(d->params), zonemap);
338
339 const int size = d->params.size;
340
341 // precompute scale and offset
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]);
345}
346
348{
350 piece->data_size = sizeof(dt_iop_zonesystem_data_t);
351}
352
354{
355 dt_free_align(piece->data);
356 piece->data = NULL;
357}
358
359void gui_update(struct dt_iop_module_t *self)
360{
361 // dt_iop_module_t *module = (dt_iop_module_t *)self;
363 // dt_iop_zonesystem_params_t *p = (dt_iop_zonesystem_params_t *)module->params;
364 gtk_widget_queue_draw(GTK_WIDGET(g->zones));
365}
366
367static void _iop_zonesystem_redraw_preview_callback(gpointer instance, gpointer user_data);
368
369static gboolean dt_iop_zonesystem_preview_draw(GtkWidget *widget, cairo_t *crf, dt_iop_module_t *self);
370
371static gboolean dt_iop_zonesystem_bar_draw(GtkWidget *widget, cairo_t *crf, dt_iop_module_t *self);
372static gboolean dt_iop_zonesystem_bar_motion_notify(GtkWidget *widget, GdkEventMotion *event,
373 dt_iop_module_t *self);
374static gboolean dt_iop_zonesystem_bar_leave_notify(GtkWidget *widget, GdkEventCrossing *event,
375 dt_iop_module_t *self);
376static gboolean dt_iop_zonesystem_bar_button_press(GtkWidget *widget, GdkEventButton *event,
377 dt_iop_module_t *self);
378static gboolean dt_iop_zonesystem_bar_button_release(GtkWidget *widget, GdkEventButton *event,
379 dt_iop_module_t *self);
380static gboolean dt_iop_zonesystem_bar_scrolled(GtkWidget *widget, GdkEventScroll *event,
381 dt_iop_module_t *self);
382
383
384static void size_allocate_callback(GtkWidget *widget, GtkAllocation *allocation, gpointer user_data)
385{
386 dt_iop_module_t *self = (dt_iop_module_t *)user_data;
388
389 if(g->image) cairo_surface_destroy(g->image);
390 dt_free(g->image_buffer);
391
392 /* load the dt logo as a background */
393 g->image = dt_util_get_logo(MIN(allocation->width, allocation->height) * 0.75);
394 if(g->image)
395 {
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);
399 }
400 else
401 {
402 g->image_buffer = NULL;
403 g->image_width = 0;
404 g->image_height = 0;
405 }
406}
407
408void gui_init(struct dt_iop_module_t *self)
409{
411 g->in_preview_buffer = g->out_preview_buffer = NULL;
412 g->is_dragging = FALSE;
413 g->hilite_zone = FALSE;
414 g->preview_width = g->preview_height = 0;
415 g->mouse_over_output_zones = FALSE;
416
417 self->widget = gtk_box_new(GTK_ORIENTATION_VERTICAL, DT_GUI_BOX_SPACING);
418
420 g_signal_connect(G_OBJECT(g->preview), "size-allocate", G_CALLBACK(size_allocate_callback), self);
421 g_signal_connect(G_OBJECT(g->preview), "draw", G_CALLBACK(dt_iop_zonesystem_preview_draw), self);
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);
425
426 /* create the zonesystem bar widget */
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"));
431 g_signal_connect(G_OBJECT(g->zones), "draw", G_CALLBACK(dt_iop_zonesystem_bar_draw), self);
432 g_signal_connect(G_OBJECT(g->zones), "motion-notify-event", G_CALLBACK(dt_iop_zonesystem_bar_motion_notify),
433 self);
434 g_signal_connect(G_OBJECT(g->zones), "leave-notify-event", G_CALLBACK(dt_iop_zonesystem_bar_leave_notify),
435 self);
436 g_signal_connect(G_OBJECT(g->zones), "button-press-event", G_CALLBACK(dt_iop_zonesystem_bar_button_press),
437 self);
438 g_signal_connect(G_OBJECT(g->zones), "button-release-event",
439 G_CALLBACK(dt_iop_zonesystem_bar_button_release), self);
440 g_signal_connect(G_OBJECT(g->zones), "scroll-event", G_CALLBACK(dt_iop_zonesystem_bar_scrolled), self);
441 gtk_widget_add_events(GTK_WIDGET(g->zones), GDK_POINTER_MOTION_MASK
442 | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK
443 | GDK_LEAVE_NOTIFY_MASK | darktable.gui->scroll_mask);
444 gtk_widget_set_size_request(g->zones, -1, DT_PIXEL_APPLY_DPI(40));
445
446 gtk_box_pack_start(GTK_BOX(self->widget), g->preview, TRUE, TRUE, 0);
447 gtk_box_pack_start(GTK_BOX(self->widget), g->zones, TRUE, TRUE, 0);
448
449 /* add signal handler for preview pipe finish to redraw the preview */
452
453
454 g->image = NULL;
455 g->image_buffer = NULL;
456 g->image_width = 0;
457 g->image_height = 0;
458}
459
460void gui_cleanup(struct dt_iop_module_t *self)
461{
463
465 dt_free(g->in_preview_buffer);
466 dt_free(g->out_preview_buffer);
467 if(g->image) cairo_surface_destroy(g->image);
468 dt_free(g->image_buffer);
469
471}
472
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
476static gboolean dt_iop_zonesystem_bar_draw(GtkWidget *widget, cairo_t *crf, dt_iop_module_t *self)
477{
480
481 const int inset = DT_ZONESYSTEM_INSET;
482 GtkAllocation allocation;
483 gtk_widget_get_allocation(widget, &allocation);
484 int width = allocation.width, height = allocation.height;
485 cairo_surface_t *cst = dt_cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height);
486 cairo_t *cr = cairo_create(cst);
487
488 /* clear background */
489 cairo_set_source_rgb(cr, .15, .15, .15);
490 cairo_paint(cr);
491
492
493 /* translate and scale */
494 width -= 2 * inset;
495 height -= 2 * inset;
496 cairo_save(cr);
497 cairo_translate(cr, inset, inset);
498 cairo_scale(cr, width, height);
499
500 /* render the bars */
501 float zonemap[MAX_ZONE_SYSTEM_SIZE] = { 0 };
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++)
506 {
507 /* draw the reference zone */
508 float z = s * i;
509 cairo_rectangle(cr, (1. / (p->size - 1)) * i, 0, (1. / (p->size - 1)),
511 cairo_set_source_rgb(cr, z, z, z);
512 cairo_fill(cr);
513
514 /* draw zone mappings */
515 cairo_rectangle(cr, zonemap[i], DT_ZONESYSTEM_REFERENCE_SPLIT + DT_ZONESYSTEM_BAR_SPLIT_WIDTH,
516 (zonemap[i + 1] - zonemap[i]), 1.0 - DT_ZONESYSTEM_REFERENCE_SPLIT);
517 cairo_set_source_rgb(cr, z, z, z);
518 cairo_fill(cr);
519 }
520 cairo_set_antialias(cr, CAIRO_ANTIALIAS_DEFAULT);
521 cairo_restore(cr);
522
523 /* render zonebar control lines */
524 cairo_set_antialias(cr, CAIRO_ANTIALIAS_NONE);
525 cairo_set_line_width(cr, 1.);
526 cairo_rectangle(cr, inset, inset, width, height);
527 cairo_set_source_rgb(cr, .1, .1, .1);
528 cairo_stroke(cr);
529 cairo_set_antialias(cr, CAIRO_ANTIALIAS_DEFAULT);
530
531 /* render control points handles */
532 cairo_set_source_rgb(cr, 0.6, 0.6, 0.6);
533 cairo_set_line_width(cr, DT_PIXEL_APPLY_DPI(1.));
534 const float arrw = DT_PIXEL_APPLY_DPI(7.0f);
535 for(int k = 1; k < p->size - 1; k++)
536 {
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)
541 {
542 gboolean is_under_mouse = ((width * zonemap[k]) - arrw * .5f < g->mouse_x
543 && (width * zonemap[k]) + arrw * .5f > g->mouse_x);
544
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);
550
551 if(is_under_mouse)
552 cairo_fill(cr);
553 else
554 cairo_stroke(cr);
555 }
556 }
557
558
559 /* push mem surface into widget */
560 cairo_destroy(cr);
561 cairo_set_source_surface(crf, cst, 0, 0);
562 cairo_paint(crf);
563 cairo_surface_destroy(cst);
564
565 return TRUE;
566}
567
568static gboolean dt_iop_zonesystem_bar_button_press(GtkWidget *widget, GdkEventButton *event,
569 dt_iop_module_t *self)
570{
573 const int inset = DT_ZONESYSTEM_INSET;
574 GtkAllocation allocation;
575 gtk_widget_get_allocation(widget, &allocation);
576 int width = allocation.width - 2 * inset; /*, height = allocation.height - 2*inset;*/
577
578 /* calculate zonemap */
579 float zonemap[MAX_ZONE_SYSTEM_SIZE] = { -1 };
581
582 /* translate mouse into zone index */
583 int k = _iop_zonesystem_zone_index_from_lightness(g->mouse_x / width, zonemap, p->size);
584 float zw = zonemap[k + 1] - zonemap[k];
585 if((g->mouse_x / width) > zonemap[k] + (zw / 2)) k++;
586
587
588 if(event->button == 1)
589 {
590 if(p->zone[k] == -1)
591 {
592 p->zone[k] = zonemap[k];
594 }
595 g->is_dragging = TRUE;
596 g->current_zone = k;
597 }
598 else if(event->button == 3)
599 {
600 /* clear the controlpoint */
601 p->zone[k] = -1;
603 }
604
605 return TRUE;
606}
607
608static gboolean dt_iop_zonesystem_bar_button_release(GtkWidget *widget, GdkEventButton *event,
609 dt_iop_module_t *self)
610{
612 if(event->button == 1)
613 {
614 g->is_dragging = FALSE;
615 }
616 return TRUE;
617}
618
619static gboolean dt_iop_zonesystem_bar_scrolled(GtkWidget *widget, GdkEventScroll *event, dt_iop_module_t *self)
620{
622 int cs = CLAMP(p->size, 4, MAX_ZONE_SYSTEM_SIZE);
623
624 int delta_y;
625 if(dt_gui_get_scroll_unit_deltas(event, NULL, &delta_y))
626 {
627 p->size = CLAMP(p->size - delta_y, 4, MAX_ZONE_SYSTEM_SIZE);
628 p->zone[cs] = -1;
630 gtk_widget_queue_draw(widget);
631 }
632
633 return TRUE;
634}
635
636static gboolean dt_iop_zonesystem_bar_leave_notify(GtkWidget *widget, GdkEventCrossing *event,
637 dt_iop_module_t *self)
638{
640 g->hilite_zone = FALSE;
641 gtk_widget_queue_draw(g->preview);
642 return TRUE;
643}
644
645static gboolean dt_iop_zonesystem_bar_motion_notify(GtkWidget *widget, GdkEventMotion *event,
646 dt_iop_module_t *self)
647{
650 const int inset = DT_ZONESYSTEM_INSET;
651 GtkAllocation allocation;
652 gtk_widget_get_allocation(widget, &allocation);
653 int width = allocation.width - 2 * inset, height = allocation.height - 2 * inset;
654
655 /* calculate zonemap */
656 float zonemap[MAX_ZONE_SYSTEM_SIZE] = { -1 };
658
659 /* record mouse position within control */
660 g->mouse_x = CLAMP(event->x - inset, 0, width);
661 g->mouse_y = CLAMP(height - 1 - event->y + inset, 0, height);
662
663 if(g->is_dragging)
664 {
665 if((g->mouse_x / width) > zonemap[g->current_zone - 1]
666 && (g->mouse_x / width) < zonemap[g->current_zone + 1])
667 {
668 p->zone[g->current_zone] = (g->mouse_x / width);
670 }
671 }
672 else
673 {
674 /* decide which zone the mouse is over */
675 if(g->mouse_y >= height * (1.0 - DT_ZONESYSTEM_REFERENCE_SPLIT))
676 {
677 g->zone_under_mouse = (g->mouse_x / width) / (1.0 / (p->size - 1));
678 g->mouse_over_output_zones = TRUE;
679 }
680 else
681 {
682 float xpos = g->mouse_x / width;
683 for(int z = 0; z < p->size - 1; z++)
684 {
685 if(xpos >= zonemap[z] && xpos < zonemap[z + 1])
686 {
687 g->zone_under_mouse = z;
688 break;
689 }
690 }
691 g->mouse_over_output_zones = FALSE;
692 }
693 g->hilite_zone = (g->mouse_y < height) ? TRUE : FALSE;
694 }
695
696 gtk_widget_queue_draw(self->widget);
697 gtk_widget_queue_draw(g->preview);
698 return TRUE;
699}
700
701
702static gboolean dt_iop_zonesystem_preview_draw(GtkWidget *widget, cairo_t *crf, dt_iop_module_t *self)
703{
704 const int inset = DT_PIXEL_APPLY_DPI(2);
705 GtkAllocation allocation;
706 gtk_widget_get_allocation(widget, &allocation);
707 int width = allocation.width, height = allocation.height;
708
711
712 cairo_surface_t *cst = dt_cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height);
713 cairo_t *cr = cairo_create(cst);
714
715 /* clear background */
716 GtkStyleContext *context = gtk_widget_get_style_context(self->expander);
717 gtk_render_background(context, cr, 0, 0, allocation.width, allocation.height);
718
719 width -= 2 * inset;
720 height -= 2 * inset;
721 cairo_translate(cr, inset, inset);
722
724 if(g->in_preview_buffer && g->out_preview_buffer && self->enabled)
725 {
726 /* calculate the zonemap */
727 float zonemap[MAX_ZONE_SYSTEM_SIZE] = { -1 };
729
730 /* let's generate a pixbuf from pixel zone buffer */
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++)
734 {
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;
739 }
741
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);
746 cairo_translate(cr, width / 2.0, height / 2.0f);
747 cairo_scale(cr, scale, scale);
748 cairo_translate(cr, -.5f * wd, -.5f * ht);
749
750 cairo_rectangle(cr, DT_PIXEL_APPLY_DPI(1), DT_PIXEL_APPLY_DPI(1), wd - DT_PIXEL_APPLY_DPI(2),
751 ht - DT_PIXEL_APPLY_DPI(2));
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);
756
757 cairo_set_line_width(cr, DT_PIXEL_APPLY_DPI(1.0));
758 cairo_set_source_rgb(cr, .1, .1, .1);
759 cairo_stroke(cr);
760
761 dt_free(image);
762 }
763 else
764 {
766 // draw a big, subdued dt logo
767 if(g->image)
768 {
769 GdkRGBA *color;
770 gtk_style_context_get(context, gtk_widget_get_state_flags(self->expander), "background-color", &color,
771 NULL);
772
773 cairo_set_source_surface(cr, g->image, (width - g->image_width) * 0.5, (height - g->image_height) * 0.5);
774 cairo_rectangle(cr, 0, 0, width, height);
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);
782 cairo_fill(cr);
783
784 gdk_rgba_free(color);
785 }
786 }
787
788 cairo_destroy(cr);
789 cairo_set_source_surface(crf, cst, 0, 0);
790 cairo_paint(crf);
791 cairo_surface_destroy(cst);
792
793 return TRUE;
794}
795
796void _iop_zonesystem_redraw_preview_callback(gpointer instance, gpointer user_data)
797{
798 dt_iop_module_t *self = (dt_iop_module_t *)user_data;
800
802}
803
804// clang-format off
805// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
806// vim: shiftwidth=2 expandtab tabstop=2 cindent
807// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
808// clang-format on
#define TRUE
Definition ashift_lsd.c:162
#define FALSE
Definition ashift_lsd.c:158
int width
Definition bilateral.h:1
int height
Definition bilateral.h:1
static const dt_aligned_pixel_simd_t const dt_adaptation_t const float p
@ IOP_CS_LAB
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...
Definition control.c:906
darktable_t darktable
Definition darktable.c:181
#define dt_free_align(ptr)
Definition darktable.h:481
static void * dt_calloc_align(size_t size)
Definition darktable.h:488
#define for_each_channel(_var,...)
Definition darktable.h:662
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...
Definition darktable.h:524
#define dt_free(ptr)
Definition darktable.h:456
#define DT_MODULE_INTROSPECTION(MODVER, PARAMSTYPE)
Definition darktable.h:151
#define __DT_CLONE_TARGETS__
Definition darktable.h:367
#define __OMP_PARALLEL_FOR__(...)
Definition darktable.h:258
#define IS_NULL_PTR(p)
C is way too permissive with !=, == and if(var) checks, which can mean too many things depending on w...
Definition darktable.h:281
#define dt_dev_add_history_item(dev, module, enable, redraw)
void dt_iop_params_t
Definition dev_history.h:41
gboolean dt_dev_pixelpipe_has_preview_output(const dt_develop_t *dev, const dt_dev_pixelpipe_t *pipe, const dt_iop_roi_t *roi)
Definition develop.c:367
@ DT_DEV_PIXELPIPE_DISPLAY_MASK
Definition develop.h:118
GtkWidget * dtgtk_drawing_area_new_with_aspect_ratio(double aspect)
Definition drawingarea.c:54
void dt_gaussian_free(dt_gaussian_t *g)
Definition gaussian.c:330
__DT_CLONE_TARGETS__ void dt_gaussian_blur(dt_gaussian_t *g, const float *const in, float *const out)
Definition gaussian.c:171
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)
Definition gaussian.c:122
@ DT_IOP_GAUSSIAN_ZERO
Definition gaussian.h:33
gboolean dt_gui_get_scroll_unit_deltas(const GdkEventScroll *event, int *delta_x, int *delta_y)
Definition gtk.c:219
static cairo_surface_t * dt_cairo_image_surface_create(cairo_format_t format, int width, int height)
Definition gtk.h:316
#define DT_GUI_BOX_SPACING
Definition gtk.h:109
#define DT_PIXEL_APPLY_DPI(value)
Definition gtk.h:90
#define IOP_GUI_FREE
Definition imageop.h:602
static void dt_iop_gui_enter_critical_section(dt_iop_module_t *const module) ACQUIRE(&module -> gui_lock)
Definition imageop.h:413
@ IOP_FLAGS_INCLUDE_IN_STYLES
Definition imageop.h:166
@ IOP_FLAGS_DEPRECATED
Definition imageop.h:168
@ IOP_FLAGS_SUPPORTS_BLENDING
Definition imageop.h:167
@ IOP_FLAGS_ALLOW_TILING
Definition imageop.h:169
static void dt_iop_gui_leave_critical_section(dt_iop_module_t *const module) RELEASE(&module -> gui_lock)
Definition imageop.h:419
@ IOP_GROUP_TONES
Definition imageop.h:137
#define IOP_GUI_ALLOC(module)
Definition imageop.h:599
void *const ovoid
float *const restrict const size_t k
float *const restrict const size_t const size_t ch
#define CLAMPS(A, L, H)
Definition math.h:76
#define CLIP(x)
Definition math.h:81
size_t size
Definition mipmap_cache.c:3
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)
Definition signal.h:368
@ DT_SIGNAL_DEVELOP_PREVIEW_PIPE_FINISHED
This signal is raised when develop preview pipe process is finished no param, no returned value.
Definition signal.h:174
#define DT_DEBUG_CONTROL_SIGNAL_CONNECT(ctlsig, signal, cb, user_data)
Definition signal.h:357
struct _GtkWidget GtkWidget
Definition splash.h:29
const float sigma
struct dt_gui_gtk_t * gui
Definition darktable.h:775
struct dt_control_signal_t * signals
Definition darktable.h:774
struct dt_develop_t * develop
Definition darktable.h:770
dt_iop_buffer_dsc_t dsc_in
struct dt_iop_module_t *void * data
int32_t gui_attached
Definition develop.h:162
gint scroll_mask
Definition gtk.h:224
unsigned int channels
Definition format.h:54
GtkWidget * widget
Definition imageop.h:337
struct dt_develop_t * dev
Definition imageop.h:296
dt_iop_gui_data_t * gui_data
Definition imageop.h:311
gboolean enabled
Definition imageop.h:298
GtkWidget * expander
Definition imageop.h:345
dt_iop_params_t * params
Definition imageop.h:307
Region of interest passed through the pixelpipe.
Definition imageop.h:72
double scale
Definition imageop.h:74
dt_iop_zonesystem_params_t params
Definition zonesystem.c:95
cairo_surface_t * image
Definition zonesystem.c:130
#define MIN(a, b)
Definition thinplate.c:32
cairo_surface_t * dt_util_get_logo(const float size)
Definition utility.c:521
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)
Definition zonesystem.c:325
static gboolean dt_iop_zonesystem_bar_leave_notify(GtkWidget *widget, GdkEventCrossing *event, dt_iop_module_t *self)
Definition zonesystem.c:636
int default_group()
Definition zonesystem.c:153
__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)
Definition zonesystem.c:295
#define DT_ZONESYSTEM_BAR_SPLIT_WIDTH
Definition zonesystem.c:474
static gboolean dt_iop_zonesystem_bar_button_press(GtkWidget *widget, GdkEventButton *event, dt_iop_module_t *self)
Definition zonesystem.c:568
static gboolean dt_iop_zonesystem_bar_draw(GtkWidget *widget, cairo_t *crf, dt_iop_module_t *self)
Definition zonesystem.c:476
#define DT_ZONESYSTEM_REFERENCE_SPLIT
Definition zonesystem.c:475
static gboolean dt_iop_zonesystem_preview_draw(GtkWidget *widget, cairo_t *crf, dt_iop_module_t *self)
Definition zonesystem.c:702
void init_pipe(struct dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
Definition zonesystem.c:347
const char * name()
Definition zonesystem.c:137
void gui_update(struct dt_iop_module_t *self)
Refresh GUI controls from current params and configuration.
Definition zonesystem.c:359
static gboolean dt_iop_zonesystem_bar_scrolled(GtkWidget *widget, GdkEventScroll *event, dt_iop_module_t *self)
Definition zonesystem.c:619
static void _iop_zonesystem_calculate_zonemap(struct dt_iop_zonesystem_params_t *p, float *zonemap)
Definition zonesystem.c:172
static int _iop_zonesystem_zone_index_from_lightness(float lightness, float *zonemap, int size)
Definition zonesystem.c:164
void gui_init(struct dt_iop_module_t *self)
Definition zonesystem.c:408
static void _iop_zonesystem_redraw_preview_callback(gpointer instance, gpointer user_data)
Definition zonesystem.c:796
#define DT_ZONESYSTEM_INSET
Definition zonesystem.c:473
static void size_allocate_callback(GtkWidget *widget, GtkAllocation *allocation, gpointer user_data)
Definition zonesystem.c:384
int default_colorspace(dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece)
Definition zonesystem.c:158
int flags()
Definition zonesystem.c:142
void gui_cleanup(struct dt_iop_module_t *self)
Definition zonesystem.c:460
static gboolean dt_iop_zonesystem_bar_button_release(GtkWidget *widget, GdkEventButton *event, dt_iop_module_t *self)
Definition zonesystem.c:608
void cleanup_pipe(struct dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
Definition zonesystem.c:353
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)
Definition zonesystem.c:226
const char * deprecated_msg()
Definition zonesystem.c:148
static gboolean dt_iop_zonesystem_bar_motion_notify(GtkWidget *widget, GdkEventMotion *event, dt_iop_module_t *self)
Definition zonesystem.c:645
#define MAX_ZONE_SYSTEM_SIZE
Definition zonesystem.c:83