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 {
210 if(IS_NULL_PTR(g)) return;
211
213 if(IS_NULL_PTR(g->in_preview_buffer) || IS_NULL_PTR(g->out_preview_buffer) || g->preview_width != width
214 || g->preview_height != height)
215 {
216 dt_free(g->in_preview_buffer);
217 dt_free(g->out_preview_buffer);
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));
220 g->preview_width = width;
221 g->preview_height = height;
222 }
224 }
225}
226
228static void process_common_cleanup(struct dt_iop_module_t *self, const dt_dev_pixelpipe_t *pipe,
229 const dt_dev_pixelpipe_iop_t *piece,
230 const void *const ivoid, void *const ovoid,
231 const dt_iop_roi_t *const roi_in, const dt_iop_roi_t *const roi_out)
232{
235
236 const int width = roi_out->width;
237 const int height = roi_out->height;
238 const size_t ch = piece->dsc_in.channels;
239 const int size = d->params.size;
240
241 if(pipe->mask_display & DT_DEV_PIXELPIPE_DISPLAY_MASK) dt_iop_alpha_copy(ivoid, ovoid, width, height);
242
243 /* if gui and have buffer lets gaussblur and fill buffer with zone indexes */
244 if(self->dev->gui_attached
245 && dt_dev_pixelpipe_has_preview_output(self->dev, pipe, roi_out)
246 && !IS_NULL_PTR(g) && g->in_preview_buffer
247 && g->out_preview_buffer)
248 {
249 float Lmax[] = { 100.0f };
250 float Lmin[] = { 0.0f };
251
252 /* setup gaussian kernel */
253 const int radius = 8;
254 const float sigma = 2.5 * (radius * roi_in->scale);
255
257
258 float *tmp = g_malloc_n((size_t)width * height, sizeof(float));
259
260 if(gauss && !IS_NULL_PTR(tmp))
261 {
263 for(size_t k = 0; k < (size_t)width * height; k++) tmp[k] = ((float *)ivoid)[ch * k];
264
265 dt_gaussian_blur(gauss, tmp, tmp);
266
267 /* create zonemap preview for input */
270 for(size_t k = 0; k < (size_t)width * height; k++)
271 {
272 g->in_preview_buffer[k] = CLAMPS(tmp[k] * (size - 1) / 100.0f, 0, size - 2);
273 }
276 for(size_t k = 0; k < (size_t)width * height; k++) tmp[k] = ((float *)ovoid)[ch * k];
277
278 dt_gaussian_blur(gauss, tmp, tmp);
279
280
281 /* create zonemap preview for output */
284 for(size_t k = 0; k < (size_t)width * height; k++)
285 {
286 g->out_preview_buffer[k] = CLAMPS(tmp[k] * (size - 1) / 100.0f, 0, size - 2);
287 }
289 }
290
291 dt_free(tmp);
292 if(gauss) dt_gaussian_free(gauss);
293 }
294}
295
297int process(struct dt_iop_module_t *self, const dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece, const void *const ivoid,
298 void *const ovoid)
299{
300 const dt_iop_roi_t *const roi_in = &piece->roi_in;
301 const dt_iop_roi_t *const roi_out = &piece->roi_out;
302
303 const dt_iop_zonesystem_data_t *const d = (const dt_iop_zonesystem_data_t *const)piece->data;
304 process_common_setup(self, pipe, piece, ivoid, ovoid, roi_in, roi_out);
305
306 const int size = d->params.size;
307
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)
313 {
314 /* remap lightness into zonemap and apply lightness */
315 const int rz = CLAMPS(in[k] * d->rzscale, 0, size - 2); // zone index
316 const float zs = ((rz > 0) ? (d->zonemap_offset[rz] / in[k]) : 0) + d->zonemap_scale[rz];
317 for_each_channel(c,aligned(in,out))
318 {
319 out[k+c] = in[k+c] * zs;
320 }
321 }
322
323 process_common_cleanup(self, pipe, piece, ivoid, ovoid, roi_in, roi_out);
324 return 0;
325}
326
329{
331
333
334 d->params = *p;
335 d->rzscale = (d->params.size - 1) / 100.0f;
336
337 /* calculate zonemap */
338 float zonemap[MAX_ZONE_SYSTEM_SIZE] = { -1 };
339 _iop_zonesystem_calculate_zonemap(&(d->params), zonemap);
340
341 const int size = d->params.size;
342
343 // precompute scale and offset
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]);
347}
348
350{
352 piece->data_size = sizeof(dt_iop_zonesystem_data_t);
353}
354
356{
357 dt_free_align(piece->data);
358 piece->data = NULL;
359}
360
361void gui_update(struct dt_iop_module_t *self)
362{
363 // dt_iop_module_t *module = (dt_iop_module_t *)self;
365 // dt_iop_zonesystem_params_t *p = (dt_iop_zonesystem_params_t *)module->params;
366 gtk_widget_queue_draw(GTK_WIDGET(g->zones));
367}
368
369static void _iop_zonesystem_redraw_preview_callback(gpointer instance, gpointer user_data);
370
371static gboolean dt_iop_zonesystem_preview_draw(GtkWidget *widget, cairo_t *crf, dt_iop_module_t *self);
372
373static gboolean dt_iop_zonesystem_bar_draw(GtkWidget *widget, cairo_t *crf, dt_iop_module_t *self);
374static gboolean dt_iop_zonesystem_bar_motion_notify(GtkWidget *widget, GdkEventMotion *event,
375 dt_iop_module_t *self);
376static gboolean dt_iop_zonesystem_bar_leave_notify(GtkWidget *widget, GdkEventCrossing *event,
377 dt_iop_module_t *self);
378static gboolean dt_iop_zonesystem_bar_button_press(GtkWidget *widget, GdkEventButton *event,
379 dt_iop_module_t *self);
380static gboolean dt_iop_zonesystem_bar_button_release(GtkWidget *widget, GdkEventButton *event,
381 dt_iop_module_t *self);
382static gboolean dt_iop_zonesystem_bar_scrolled(GtkWidget *widget, GdkEventScroll *event,
383 dt_iop_module_t *self);
384
385
386static void size_allocate_callback(GtkWidget *widget, GtkAllocation *allocation, gpointer user_data)
387{
388 dt_iop_module_t *self = (dt_iop_module_t *)user_data;
390
391 if(g->image) cairo_surface_destroy(g->image);
392 dt_free(g->image_buffer);
393
394 /* load the dt logo as a background */
395 g->image = dt_util_get_logo(MIN(allocation->width, allocation->height) * 0.75);
396 if(g->image)
397 {
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);
401 }
402 else
403 {
404 g->image_buffer = NULL;
405 g->image_width = 0;
406 g->image_height = 0;
407 }
408}
409
410void gui_init(struct dt_iop_module_t *self)
411{
413 g->in_preview_buffer = g->out_preview_buffer = NULL;
414 g->is_dragging = FALSE;
415 g->hilite_zone = FALSE;
416 g->preview_width = g->preview_height = 0;
417 g->mouse_over_output_zones = FALSE;
418
419 self->widget = gtk_box_new(GTK_ORIENTATION_VERTICAL, DT_GUI_BOX_SPACING);
420
422 g_signal_connect(G_OBJECT(g->preview), "size-allocate", G_CALLBACK(size_allocate_callback), self);
423 g_signal_connect(G_OBJECT(g->preview), "draw", G_CALLBACK(dt_iop_zonesystem_preview_draw), self);
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);
427
428 /* create the zonesystem bar widget */
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"));
433 g_signal_connect(G_OBJECT(g->zones), "draw", G_CALLBACK(dt_iop_zonesystem_bar_draw), self);
434 g_signal_connect(G_OBJECT(g->zones), "motion-notify-event", G_CALLBACK(dt_iop_zonesystem_bar_motion_notify),
435 self);
436 g_signal_connect(G_OBJECT(g->zones), "leave-notify-event", G_CALLBACK(dt_iop_zonesystem_bar_leave_notify),
437 self);
438 g_signal_connect(G_OBJECT(g->zones), "button-press-event", G_CALLBACK(dt_iop_zonesystem_bar_button_press),
439 self);
440 g_signal_connect(G_OBJECT(g->zones), "button-release-event",
441 G_CALLBACK(dt_iop_zonesystem_bar_button_release), self);
442 g_signal_connect(G_OBJECT(g->zones), "scroll-event", G_CALLBACK(dt_iop_zonesystem_bar_scrolled), self);
443 gtk_widget_add_events(GTK_WIDGET(g->zones), GDK_POINTER_MOTION_MASK
444 | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK
445 | GDK_LEAVE_NOTIFY_MASK | darktable.gui->scroll_mask);
446 gtk_widget_set_size_request(g->zones, -1, DT_PIXEL_APPLY_DPI(40));
447
448 gtk_box_pack_start(GTK_BOX(self->widget), g->preview, TRUE, TRUE, 0);
449 gtk_box_pack_start(GTK_BOX(self->widget), g->zones, TRUE, TRUE, 0);
450
451 /* add signal handler for preview pipe finish to redraw the preview */
454
455
456 g->image = NULL;
457 g->image_buffer = NULL;
458 g->image_width = 0;
459 g->image_height = 0;
460}
461
462void gui_cleanup(struct dt_iop_module_t *self)
463{
465
467 dt_free(g->in_preview_buffer);
468 dt_free(g->out_preview_buffer);
469 if(g->image) cairo_surface_destroy(g->image);
470 dt_free(g->image_buffer);
471
473}
474
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
478static gboolean dt_iop_zonesystem_bar_draw(GtkWidget *widget, cairo_t *crf, dt_iop_module_t *self)
479{
482
483 const int inset = DT_ZONESYSTEM_INSET;
484 GtkAllocation allocation;
485 gtk_widget_get_allocation(widget, &allocation);
486 int width = allocation.width, height = allocation.height;
487 cairo_surface_t *cst = dt_cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height);
488 cairo_t *cr = cairo_create(cst);
489
490 /* clear background */
491 cairo_set_source_rgb(cr, .15, .15, .15);
492 cairo_paint(cr);
493
494
495 /* translate and scale */
496 width -= 2 * inset;
497 height -= 2 * inset;
498 cairo_save(cr);
499 cairo_translate(cr, inset, inset);
500 cairo_scale(cr, width, height);
501
502 /* render the bars */
503 float zonemap[MAX_ZONE_SYSTEM_SIZE] = { 0 };
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++)
508 {
509 /* draw the reference zone */
510 float z = s * i;
511 cairo_rectangle(cr, (1. / (p->size - 1)) * i, 0, (1. / (p->size - 1)),
513 cairo_set_source_rgb(cr, z, z, z);
514 cairo_fill(cr);
515
516 /* draw zone mappings */
517 cairo_rectangle(cr, zonemap[i], DT_ZONESYSTEM_REFERENCE_SPLIT + DT_ZONESYSTEM_BAR_SPLIT_WIDTH,
518 (zonemap[i + 1] - zonemap[i]), 1.0 - DT_ZONESYSTEM_REFERENCE_SPLIT);
519 cairo_set_source_rgb(cr, z, z, z);
520 cairo_fill(cr);
521 }
522 cairo_set_antialias(cr, CAIRO_ANTIALIAS_DEFAULT);
523 cairo_restore(cr);
524
525 /* render zonebar control lines */
526 cairo_set_antialias(cr, CAIRO_ANTIALIAS_NONE);
527 cairo_set_line_width(cr, 1.);
528 cairo_rectangle(cr, inset, inset, width, height);
529 cairo_set_source_rgb(cr, .1, .1, .1);
530 cairo_stroke(cr);
531 cairo_set_antialias(cr, CAIRO_ANTIALIAS_DEFAULT);
532
533 /* render control points handles */
534 cairo_set_source_rgb(cr, 0.6, 0.6, 0.6);
535 cairo_set_line_width(cr, DT_PIXEL_APPLY_DPI(1.));
536 const float arrw = DT_PIXEL_APPLY_DPI(7.0f);
537 for(int k = 1; k < p->size - 1; k++)
538 {
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)
543 {
544 gboolean is_under_mouse = ((width * zonemap[k]) - arrw * .5f < g->mouse_x
545 && (width * zonemap[k]) + arrw * .5f > g->mouse_x);
546
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);
552
553 if(is_under_mouse)
554 cairo_fill(cr);
555 else
556 cairo_stroke(cr);
557 }
558 }
559
560
561 /* push mem surface into widget */
562 cairo_destroy(cr);
563 cairo_set_source_surface(crf, cst, 0, 0);
564 cairo_paint(crf);
565 cairo_surface_destroy(cst);
566
567 return TRUE;
568}
569
570static gboolean dt_iop_zonesystem_bar_button_press(GtkWidget *widget, GdkEventButton *event,
571 dt_iop_module_t *self)
572{
575 const int inset = DT_ZONESYSTEM_INSET;
576 GtkAllocation allocation;
577 gtk_widget_get_allocation(widget, &allocation);
578 int width = allocation.width - 2 * inset; /*, height = allocation.height - 2*inset;*/
579
580 /* calculate zonemap */
581 float zonemap[MAX_ZONE_SYSTEM_SIZE] = { -1 };
583
584 /* translate mouse into zone index */
585 int k = _iop_zonesystem_zone_index_from_lightness(g->mouse_x / width, zonemap, p->size);
586 float zw = zonemap[k + 1] - zonemap[k];
587 if((g->mouse_x / width) > zonemap[k] + (zw / 2)) k++;
588
589
590 if(event->button == 1)
591 {
592 if(p->zone[k] == -1)
593 {
594 p->zone[k] = zonemap[k];
596 }
597 g->is_dragging = TRUE;
598 g->current_zone = k;
599 }
600 else if(event->button == 3)
601 {
602 /* clear the controlpoint */
603 p->zone[k] = -1;
605 }
606
607 return TRUE;
608}
609
610static gboolean dt_iop_zonesystem_bar_button_release(GtkWidget *widget, GdkEventButton *event,
611 dt_iop_module_t *self)
612{
614 if(event->button == 1)
615 {
616 g->is_dragging = FALSE;
617 }
618 return TRUE;
619}
620
621static gboolean dt_iop_zonesystem_bar_scrolled(GtkWidget *widget, GdkEventScroll *event, dt_iop_module_t *self)
622{
624 int cs = CLAMP(p->size, 4, MAX_ZONE_SYSTEM_SIZE);
625
626 int delta_y;
627 if(dt_gui_get_scroll_unit_deltas(event, NULL, &delta_y))
628 {
629 p->size = CLAMP(p->size - delta_y, 4, MAX_ZONE_SYSTEM_SIZE);
630 p->zone[cs] = -1;
632 gtk_widget_queue_draw(widget);
633 }
634
635 return TRUE;
636}
637
638static gboolean dt_iop_zonesystem_bar_leave_notify(GtkWidget *widget, GdkEventCrossing *event,
639 dt_iop_module_t *self)
640{
642 g->hilite_zone = FALSE;
643 gtk_widget_queue_draw(g->preview);
644 return TRUE;
645}
646
647static gboolean dt_iop_zonesystem_bar_motion_notify(GtkWidget *widget, GdkEventMotion *event,
648 dt_iop_module_t *self)
649{
652 const int inset = DT_ZONESYSTEM_INSET;
653 GtkAllocation allocation;
654 gtk_widget_get_allocation(widget, &allocation);
655 int width = allocation.width - 2 * inset, height = allocation.height - 2 * inset;
656
657 /* calculate zonemap */
658 float zonemap[MAX_ZONE_SYSTEM_SIZE] = { -1 };
660
661 /* record mouse position within control */
662 g->mouse_x = CLAMP(event->x - inset, 0, width);
663 g->mouse_y = CLAMP(height - 1 - event->y + inset, 0, height);
664
665 if(g->is_dragging)
666 {
667 if((g->mouse_x / width) > zonemap[g->current_zone - 1]
668 && (g->mouse_x / width) < zonemap[g->current_zone + 1])
669 {
670 p->zone[g->current_zone] = (g->mouse_x / width);
672 }
673 }
674 else
675 {
676 /* decide which zone the mouse is over */
677 if(g->mouse_y >= height * (1.0 - DT_ZONESYSTEM_REFERENCE_SPLIT))
678 {
679 g->zone_under_mouse = (g->mouse_x / width) / (1.0 / (p->size - 1));
680 g->mouse_over_output_zones = TRUE;
681 }
682 else
683 {
684 float xpos = g->mouse_x / width;
685 for(int z = 0; z < p->size - 1; z++)
686 {
687 if(xpos >= zonemap[z] && xpos < zonemap[z + 1])
688 {
689 g->zone_under_mouse = z;
690 break;
691 }
692 }
693 g->mouse_over_output_zones = FALSE;
694 }
695 g->hilite_zone = (g->mouse_y < height) ? TRUE : FALSE;
696 }
697
698 gtk_widget_queue_draw(self->widget);
699 gtk_widget_queue_draw(g->preview);
700 return TRUE;
701}
702
703
704static gboolean dt_iop_zonesystem_preview_draw(GtkWidget *widget, cairo_t *crf, dt_iop_module_t *self)
705{
706 const int inset = DT_PIXEL_APPLY_DPI(2);
707 GtkAllocation allocation;
708 gtk_widget_get_allocation(widget, &allocation);
709 int width = allocation.width, height = allocation.height;
710
713
714 cairo_surface_t *cst = dt_cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height);
715 cairo_t *cr = cairo_create(cst);
716
717 /* clear background */
718 GtkStyleContext *context = gtk_widget_get_style_context(self->expander);
719 gtk_render_background(context, cr, 0, 0, allocation.width, allocation.height);
720
721 width -= 2 * inset;
722 height -= 2 * inset;
723 cairo_translate(cr, inset, inset);
724
726 if(g->in_preview_buffer && g->out_preview_buffer && self->enabled)
727 {
728 /* calculate the zonemap */
729 float zonemap[MAX_ZONE_SYSTEM_SIZE] = { -1 };
731
732 /* let's generate a pixbuf from pixel zone buffer */
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++)
736 {
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;
741 }
743
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);
748 cairo_translate(cr, width / 2.0, height / 2.0f);
749 cairo_scale(cr, scale, scale);
750 cairo_translate(cr, -.5f * wd, -.5f * ht);
751
752 cairo_rectangle(cr, DT_PIXEL_APPLY_DPI(1), DT_PIXEL_APPLY_DPI(1), wd - DT_PIXEL_APPLY_DPI(2),
753 ht - DT_PIXEL_APPLY_DPI(2));
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);
758
759 cairo_set_line_width(cr, DT_PIXEL_APPLY_DPI(1.0));
760 cairo_set_source_rgb(cr, .1, .1, .1);
761 cairo_stroke(cr);
762
763 dt_free(image);
764 }
765 else
766 {
768 // draw a big, subdued dt logo
769 if(g->image)
770 {
771 GdkRGBA *color;
772 gtk_style_context_get(context, gtk_widget_get_state_flags(self->expander), "background-color", &color,
773 NULL);
774
775 cairo_set_source_surface(cr, g->image, (width - g->image_width) * 0.5, (height - g->image_height) * 0.5);
776 cairo_rectangle(cr, 0, 0, width, height);
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);
784 cairo_fill(cr);
785
786 gdk_rgba_free(color);
787 }
788 }
789
790 cairo_destroy(cr);
791 cairo_set_source_surface(crf, cst, 0, 0);
792 cairo_paint(crf);
793 cairo_surface_destroy(cst);
794
795 return TRUE;
796}
797
798void _iop_zonesystem_redraw_preview_callback(gpointer instance, gpointer user_data)
799{
800 dt_iop_module_t *self = (dt_iop_module_t *)user_data;
802
804}
805
806// clang-format off
807// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
808// vim: shiftwidth=2 expandtab tabstop=2 cindent
809// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
810// 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:922
darktable_t darktable
Definition darktable.c:183
#define dt_free_align(ptr)
Definition darktable.h:503
static void * dt_calloc_align(size_t size)
Definition darktable.h:510
#define for_each_channel(_var,...)
Definition darktable.h:684
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:546
#define dt_free(ptr)
Definition darktable.h:478
#define DT_MODULE_INTROSPECTION(MODVER, PARAMSTYPE)
Definition darktable.h:151
#define __DT_CLONE_TARGETS__
Definition darktable.h:379
#define __OMP_PARALLEL_FOR__(...)
Definition darktable.h:270
#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:293
#define dt_dev_add_history_item(dev, module, enable, redraw)
void dt_iop_params_t
Definition dev_history.h:42
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:414
@ 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:300
static cairo_surface_t * dt_cairo_image_surface_create(cairo_format_t format, int width, int height)
Definition gtk.h:322
#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:641
static void dt_iop_gui_enter_critical_section(dt_iop_module_t *const module) ACQUIRE(&module -> gui_lock)
Definition imageop.h:450
@ IOP_FLAGS_INCLUDE_IN_STYLES
Definition imageop.h:196
@ IOP_FLAGS_DEPRECATED
Definition imageop.h:198
@ IOP_FLAGS_SUPPORTS_BLENDING
Definition imageop.h:197
@ IOP_FLAGS_ALLOW_TILING
Definition imageop.h:199
static void dt_iop_gui_leave_critical_section(dt_iop_module_t *const module) RELEASE(&module -> gui_lock)
Definition imageop.h:456
@ IOP_GROUP_TONES
Definition imageop.h:167
#define IOP_GUI_ALLOC(module)
Definition imageop.h:638
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:387
@ 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:376
struct _GtkWidget GtkWidget
Definition splash.h:29
const float sigma
struct dt_gui_gtk_t * gui
Definition darktable.h:803
struct dt_control_signal_t * signals
Definition darktable.h:802
struct dt_develop_t * develop
Definition darktable.h:798
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:230
unsigned int channels
Definition format.h:54
GtkWidget * widget
Definition imageop.h:374
struct dt_develop_t * dev
Definition imageop.h:333
dt_iop_gui_data_t * gui_data
Definition imageop.h:348
gboolean enabled
Definition imageop.h:335
GtkWidget * expander
Definition imageop.h:382
dt_iop_params_t * params
Definition imageop.h:344
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:327
static gboolean dt_iop_zonesystem_bar_leave_notify(GtkWidget *widget, GdkEventCrossing *event, dt_iop_module_t *self)
Definition zonesystem.c:638
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:297
#define DT_ZONESYSTEM_BAR_SPLIT_WIDTH
Definition zonesystem.c:476
static gboolean dt_iop_zonesystem_bar_button_press(GtkWidget *widget, GdkEventButton *event, dt_iop_module_t *self)
Definition zonesystem.c:570
static gboolean dt_iop_zonesystem_bar_draw(GtkWidget *widget, cairo_t *crf, dt_iop_module_t *self)
Definition zonesystem.c:478
#define DT_ZONESYSTEM_REFERENCE_SPLIT
Definition zonesystem.c:477
static gboolean dt_iop_zonesystem_preview_draw(GtkWidget *widget, cairo_t *crf, dt_iop_module_t *self)
Definition zonesystem.c:704
void init_pipe(struct dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
Definition zonesystem.c:349
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:361
static gboolean dt_iop_zonesystem_bar_scrolled(GtkWidget *widget, GdkEventScroll *event, dt_iop_module_t *self)
Definition zonesystem.c:621
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:410
static void _iop_zonesystem_redraw_preview_callback(gpointer instance, gpointer user_data)
Definition zonesystem.c:798
#define DT_ZONESYSTEM_INSET
Definition zonesystem.c:475
static void size_allocate_callback(GtkWidget *widget, GtkAllocation *allocation, gpointer user_data)
Definition zonesystem.c:386
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:462
static gboolean dt_iop_zonesystem_bar_button_release(GtkWidget *widget, GdkEventButton *event, dt_iop_module_t *self)
Definition zonesystem.c:610
void cleanup_pipe(struct dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
Definition zonesystem.c:355
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:228
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:647
#define MAX_ZONE_SYSTEM_SIZE
Definition zonesystem.c:83