Ansel 0.0
A darktable fork - bloat + design vision
Loading...
Searching...
No Matches
darkroom.c
Go to the documentation of this file.
1/*
2 This file is part of darktable,
3 Copyright (C) 2009-2014, 2018 johannes hanika.
4 Copyright (C) 2010-2012 Henrik Andersson.
5 Copyright (C) 2010 Stuart Henderson.
6 Copyright (C) 2010-2018, 2020 Tobias Ellinghaus.
7 Copyright (C) 2011-2012 Antony Dovgal.
8 Copyright (C) 2011 Edouard Gomez.
9 Copyright (C) 2011, 2013-2015 Jérémy Rosen.
10 Copyright (C) 2011 Karl Mikaelsson.
11 Copyright (C) 2011 Omari Stephens.
12 Copyright (C) 2011 Robert Bieber.
13 Copyright (C) 2011 Steven Carter.
14 Copyright (C) 2012-2016, 2019-2022 Aldric Renaudin.
15 Copyright (C) 2012 Christian Tellefsen.
16 Copyright (C) 2012 Frédéric Grollier.
17 Copyright (C) 2012-2013 José Carlos García Sogo.
18 Copyright (C) 2012-2022 Pascal Obry.
19 Copyright (C) 2012 Richard Wonka.
20 Copyright (C) 2012-2014 Ulrich Pegelow.
21 Copyright (C) 2013 Dennis Gnad.
22 Copyright (C) 2013 Pascal de Bruijn.
23 Copyright (C) 2013-2017, 2020 Roman Lebedev.
24 Copyright (C) 2014, 2017, 2020-2021 Dan Torop.
25 Copyright (C) 2014, 2017, 2019 parafin.
26 Copyright (C) 2014 Pedro Côrte-Real.
27 Copyright (C) 2014 Stéphane Gimenez.
28 Copyright (C) 2015 Guillaume Subiron.
29 Copyright (C) 2016 Asma.
30 Copyright (C) 2016 itinerarium.
31 Copyright (C) 2017-2019 Edgardo Hoszowski.
32 Copyright (C) 2017 Matthieu Moy.
33 Copyright (C) 2017-2019 Peter Budai.
34 Copyright (C) 2017 Žilvinas Žaltiena.
35 Copyright (C) 2018 Hans Rosenfeld.
36 Copyright (C) 2018 rawfiner.
37 Copyright (C) 2018 Rikard Öxler.
38 Copyright (C) 2019 Alexander Blinne.
39 Copyright (C) 2019 Alexis Mousset.
40 Copyright (C) 2019-2020, 2022-2026 Aurélien PIERRE.
41 Copyright (C) 2019, 2021 Bill Ferguson.
42 Copyright (C) 2019-2022 Diederik Ter Rahe.
43 Copyright (C) 2019-2022 Hanno Schwalm.
44 Copyright (C) 2019-2020 Heiko Bauke.
45 Copyright (C) 2019 jakubfi.
46 Copyright (C) 2019, 2021 luzpaz.
47 Copyright (C) 2019-2020 Philippe Weyland.
48 Copyright (C) 2020-2022 Chris Elston.
49 Copyright (C) 2020 GrahamByrnes.
50 Copyright (C) 2020-2021 Hubert Kowalski.
51 Copyright (C) 2020-2021 Marco.
52 Copyright (C) 2020-2021 Mark-64.
53 Copyright (C) 2020 Miloš Komarčević.
54 Copyright (C) 2020 Reinout Nonhebel.
55 Copyright (C) 2020 U-DESKTOP-HQME86J\marco.
56 Copyright (C) 2021 darkelectron.
57 Copyright (C) 2021 lhietal.
58 Copyright (C) 2021-2022 Nicolas Auffray.
59 Copyright (C) 2021-2022 Ralf Brown.
60 Copyright (C) 2021-2022 Sakari Kapanen.
61 Copyright (C) 2021 Victor Forsiuk.
62 Copyright (C) 2022 Martin Bařinka.
63 Copyright (C) 2023 Alynx Zhou.
64 Copyright (C) 2023 Luca Zulberti.
65 Copyright (C) 2023 Maurizio Paglia.
66 Copyright (C) 2023 Ricky Moon.
67 Copyright (C) 2025-2026 Guillaume Stutin.
68
69 darktable is free software: you can redistribute it and/or modify
70 it under the terms of the GNU General Public License as published by
71 the Free Software Foundation, either version 3 of the License, or
72 (at your option) any later version.
73
74 darktable is distributed in the hope that it will be useful,
75 but WITHOUT ANY WARRANTY; without even the implied warranty of
76 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
77 GNU General Public License for more details.
78
79 You should have received a copy of the GNU General Public License
80 along with darktable. If not, see <http://www.gnu.org/licenses/>.
81*/
84#include "bauhaus/bauhaus.h"
85#include "common/collection.h"
86#include "common/colorspaces.h"
87#include "common/darktable.h"
88#include "gui/gdkkeys.h"
89#include "common/debug.h"
91#include "common/history.h"
92#include "common/image_cache.h"
93#include "common/imageio.h"
94#include "common/iop-autoset.h"
96#include "common/mipmap_cache.h"
97#include "common/selection.h"
98#include "common/tags.h"
99#include "common/undo.h"
100#include "control/conf.h"
101#include "control/control.h"
102#include "control/jobs.h"
103#include "develop/blend.h"
105#include "develop/develop.h"
106#include "develop/imageop.h"
107#include "develop/supervisor.h"
108#include "develop/masks.h"
109#include "dtgtk/button.h"
110#include "dtgtk/thumbtable.h"
111
113#include "gui/draw.h"
114#include "gui/gtk.h"
115#include "gui/gui_throttle.h"
116#include "gui/guides.h"
117#include "gui/presets.h"
118#include "libs/colorpicker.h"
119#include "libs/lib.h"
120#include "views/dev_backbuf.h"
121#include "views/dev_toolbox.h"
122#include "views/view.h"
123#include "views/view_api.h"
124#ifdef GDK_WINDOWING_QUARTZ
125#include "osx/osx.h"
126#endif
127
128#include <gdk/gdkkeysyms.h>
129#include <glib.h>
130#include <math.h>
131#include <stdlib.h>
132#include <string.h>
133#include <unistd.h>
134#include <sys/types.h>
135#include <sys/stat.h>
136#include <fcntl.h>
137
138#ifndef G_SOURCE_FUNC // Defined for glib >= 2.58
139#define G_SOURCE_FUNC(f) ((GSourceFunc) (void (*)(void)) (f))
140#endif
141
142DT_MODULE(1)
143
144typedef struct coords_t
145{
146 double roi_coord[2]; // coordinates in ROI space
147 double roi_delta[2]; // delta in ROI space
148 double full_delta[2]; // delta in image space
149 double full[2]; // coordinates in image space
151
152#define DARKROOM_EDGE_PAN_INTERVAL_MS 64
153#define DARKROOM_EDGE_PAN_MARGIN_PX DT_PIXEL_APPLY_DPI(50)
154#define DARKROOM_EDGE_PAN_SPEED_PX_PER_S 360.0f
155
156/* signal handler for filmstrip image switching */
157static void _view_darkroom_filmstrip_activate_callback(gpointer instance, int32_t imgid, gpointer user_data);
158static void _darkroom_image_loaded_callback(gpointer instance, guint request_id, guint result, gpointer user_data);
159
160static void _dev_change_image(dt_view_t *self, const int32_t imgid);
162
163static int _change_scaling(dt_develop_t *dev, const float point[2], const float new_scaling);
164static void _release_expose_source_caches(void);
165static void _darkroom_set_default_cursor(dt_view_t *self, double x, double y);
166
171
177static void _darkroom_autoset_popover_refresh(gpointer instance, gpointer user_data);
178static void _darkroom_autoset_button_set_running(const gboolean running);
179
180static void _darkroom_ioporder_quickbutton_clicked(GtkButton *button, gpointer user_data)
181{
182 dt_lib_module_t *module = dt_lib_get_module("ioporder");
183 if(module && module->show_popup)
184 module->show_popup(module);
185}
186
196static void _darkroom_autoset_button_set_running(const gboolean running)
197{
199 if(_darkroom_autoset_button_is_running == running) return;
200
202
203 gtk_widget_set_sensitive(_darkroom_autoset_button, !running);
204 gtk_widget_set_tooltip_text(_darkroom_autoset_button,
205 running ? _("Autoset is running on selected modules")
206 : _("Run autoset on selected modules\nRight click for options"));
207
208 if(running)
210 else
212}
213
214const char *name(const dt_view_t *self)
215{
216 return _("Darkroom");
217}
218
219
220void init(dt_view_t *self)
221{
222 self->data = malloc(sizeof(dt_develop_t));
223 dt_dev_init((dt_develop_t *)self->data, 1);
226}
227
228uint32_t view(const dt_view_t *self)
229{
230 return DT_VIEW_DARKROOM;
231}
232
233static void _reset_edge_pan()
234{
236 if(IS_NULL_PTR(gui)) return;
237 if(gui->pan_edge.timeout_source)
238 {
239 g_source_remove(gui->pan_edge.timeout_source);
240 }
241 memset(&gui->pan_edge, 0, sizeof(gui->pan_edge));
242}
243
245{
246 dt_develop_t *dev = (dt_develop_t *)self->data;
247
248 // Cancel any pending edge pan timeout and reset the state
250
255 {
257 {
259 "darkroom-cleanup-autoset-manager");
262 }
264 {
267 }
268 g_list_free(_autoset_manager->iop_to_set);
270 _autoset_manager = NULL;
271 }
275
276 // unref the grid lines popover if needed
280 dt_dev_cleanup(dev);
281 dt_free(dev);
282}
283
284static cairo_status_t _write_snapshot_data(void *closure, const unsigned char *data, unsigned int length)
285{
286 const int fd = GPOINTER_TO_INT(closure);
287 ssize_t res = write(fd, data, length);
288 if(res != length)
289 return CAIRO_STATUS_WRITE_ERROR;
290 return CAIRO_STATUS_SUCCESS;
291}
292
297
298static gboolean _darkroom_is_only_selected_sample(gboolean is_primary_sample, dt_colorpicker_sample_t *selected_sample, gboolean display_samples)
299{
300 return !is_primary_sample && selected_sample && !display_samples;
301}
302
304 float point[2])
305{
306 point[0] = sample->point[0];
307 point[1] = sample->point[1];
309}
310
312 float box[4])
313{
314 memcpy(box, sample->box, sizeof(float) * 4);
316}
317
330static void _darkroom_pickers_draw(dt_view_t *self, cairo_t *cri,
331 int32_t width, int32_t height, int32_t pozx, int32_t pozy,
332 GSList *samples, gboolean is_primary_sample)
333{
334 if(IS_NULL_PTR(samples)) return;
335
336 dt_develop_t *dev = (dt_develop_t *)self->data;
337
338 cairo_save(cri);
339 // The colorpicker samples bounding rectangle should only be displayed inside the visible image
340
341 const double wd = dev->roi.preview_width;
342 const double ht = dev->roi.preview_height;
343 const double scale = dt_dev_get_fit_scale(dev);
344 const double lw = 1.0 / scale;
345 const double dashes[1] = { lw * 4.0 };
346
347 dt_dev_rescale_roi(dev, cri, width, height);
348
349 // makes point sample crosshair gap look nicer
350 cairo_set_line_cap(cri, CAIRO_LINE_CAP_SQUARE);
351
353 const gboolean only_selected_sample
354 = _darkroom_is_only_selected_sample(is_primary_sample, selected_sample,
356
357 for( ; samples; samples = g_slist_next(samples))
358 {
359 dt_colorpicker_sample_t *sample = samples->data;
360 if(only_selected_sample && (sample != selected_sample))
361 continue;
362
363 // The picker is at the resolution of the preview pixelpipe. This
364 // is width/2 of a preview-pipe pixel in (scaled) user space
365 // coordinates. Use half pixel width so rounding to nearest device
366 // pixel doesn't make uneven centering.
367 double half_px = 0.5;
368 const double min_half_px_device = 4.0;
369 // FIXME: instead of going to all this effort to show how error-prone a preview pipe sample can be, just produce a better point sample
370 gboolean show_preview_pixel_scale = TRUE;
371
372 // overlays are aligned with pixels for a clean look
373 if(sample->size == DT_LIB_COLORPICKER_SIZE_BOX)
374 {
375 float image_box[4] = { 0.0f };
377 double x = image_box[0] * wd, y = image_box[1] * ht,
378 w = image_box[2] * wd, h = image_box[3] * ht;
379 cairo_user_to_device(cri, &x, &y);
380 cairo_user_to_device(cri, &w, &h);
381 x=round(x+0.5)-0.5;
382 y=round(y+0.5)-0.5;
383 w=round(w+0.5)-0.5;
384 h=round(h+0.5)-0.5;
385 cairo_device_to_user(cri, &x, &y);
386 cairo_device_to_user(cri, &w, &h);
387 cairo_rectangle(cri, x, y, w - x, h - y);
388 if(is_primary_sample)
389 {
390 // handles
391 const double hw = 5. / scale;
392 cairo_rectangle(cri, x - hw, y - hw, 2. * hw, 2. * hw);
393 cairo_rectangle(cri, x - hw, h - hw, 2. * hw, 2. * hw);
394 cairo_rectangle(cri, w - hw, y - hw, 2. * hw, 2. * hw);
395 cairo_rectangle(cri, w - hw, h - hw, 2. * hw, 2. * hw);
396 }
397 }
398 else if(sample->size == DT_LIB_COLORPICKER_SIZE_POINT)
399 {
400 // FIXME: to be really accurate, the colorpicker should render precisely over the nearest pixelpipe pixel, but this gets particularly tricky to do with iop pickers with transformations after them in the pipeline
401 float image_point[2] = { 0.0f };
402 _darkroom_sample_raw_point_to_image_norm(sample, image_point);
403 double x = image_point[0] * wd;
404 double y = image_point[1] * ht;
405 cairo_user_to_device(cri, &x, &y);
406 x=round(x+0.5)-0.5;
407 y=round(y+0.5)-0.5;
408 // render picker center a reasonable size in device pixels
409 half_px = round(half_px * scale);
410 if(half_px < min_half_px_device)
411 {
412 half_px = min_half_px_device;
413 show_preview_pixel_scale = FALSE;
414 }
415 // crosshair radius
416 double crosshair = (is_primary_sample ? 4. : 5.) * half_px;
417 if(sample == selected_sample) crosshair *= 2;
418 cairo_device_to_user(cri, &x, &y);
419 cairo_device_to_user_distance(cri, &crosshair, &half_px);
420
421 // "handles"
422 if(is_primary_sample)
423 cairo_arc(cri, x, y, crosshair, 0., 2. * M_PI);
424 // crosshair
425 cairo_move_to(cri, x - crosshair, y);
426 cairo_line_to(cri, x + crosshair, y);
427 cairo_move_to(cri, x, y - crosshair);
428 cairo_line_to(cri, x, y + crosshair);
429 }
430
431 // default is to draw 1 (logical) pixel light lines with 1
432 // (logical) pixel dark outline for legibility
433 const double line_scale = (sample == selected_sample ? 2.0 : 1.0);
434 cairo_set_line_width(cri, lw * 3.0 * line_scale);
435 cairo_set_source_rgba(cri, 0.0, 0.0, 0.0, 0.4);
436 cairo_stroke_preserve(cri);
437
438 const gboolean draw_dashed = !is_primary_sample
439 && sample != selected_sample
440 && sample->size == DT_LIB_COLORPICKER_SIZE_BOX;
441 cairo_set_line_width(cri, lw * line_scale);
442 cairo_set_dash(cri, dashes, draw_dashed, 0.0);
443
444 cairo_set_source_rgba(cri, 1.0, 1.0, 1.0, 0.8);
445 cairo_stroke(cri);
446
447 // draw the actual color sampled
448 // FIXME: if an area sample is selected, when selected should fill it with colorpicker color?
449 // NOTE: The sample may be based on outdated data, but still
450 // display as it will update eventually. If we only drew on valid
451 // data, swatches on point live samples would flicker when the
452 // primary sample was drawn, and the primary sample swatch would
453 // flicker when an iop is adjusted.
455 {
456 float image_point[2] = { 0.0f };
457 _darkroom_sample_raw_point_to_image_norm(sample, image_point);
458 if(sample == selected_sample)
459 cairo_arc(cri, image_point[0] * wd, image_point[1] * ht, half_px * 2., 0., 2. * M_PI);
460 else if(show_preview_pixel_scale)
461 cairo_rectangle(cri, image_point[0] * wd - half_px, image_point[1] * ht - half_px, half_px * 2., half_px * 2.);
462 else
463 cairo_arc(cri, image_point[0] * wd, image_point[1] * ht, half_px, 0., 2. * M_PI);
464
465 set_color(cri, sample->swatch);
466 cairo_fill(cri);
467 }
468 }
469
470 cairo_restore(cri);
471}
472
473/* Debug-only darkroom expose mode:
474 * - no persistent surface locks,
475 * - no fallback cache,
476 * - no preview substitution,
477 * - only main backbuffer if currently available.
478 *
479 * Set to 1 for baseline debugging when troubleshooting display regressions. */
480#ifndef DARKROOM_EXPOSE_DUMB_DEBUG
481#define DARKROOM_EXPOSE_DUMB_DEBUG 0
482#endif
483
484#if DARKROOM_EXPOSE_DUMB_DEBUG
485static dt_dev_pixelpipe_cache_wait_t _darkroom_main_debug_wait = { 0 };
486
487static void _darkroom_debug_restart_cache_wait(gpointer user_data)
488{
489 dt_develop_t *dev = (dt_develop_t *)user_data;
490 if(IS_NULL_PTR(dev) || !dev->gui_attached) return;
492}
493
494static gboolean _render_main_direct_debug(cairo_t *cr, dt_develop_t *dev, const int width, const int height,
495 const int border, const dt_aligned_pixel_t bg_color)
496{
497 if(IS_NULL_PTR(cr) || IS_NULL_PTR(dev) || IS_NULL_PTR(dev->pipe)) return FALSE;
498
499 cairo_set_source_rgb(cr, bg_color[0], bg_color[1], bg_color[2]);
500 cairo_paint(cr);
501
502 if(!dt_dev_pixelpipe_is_backbufer_valid(dev->pipe, dev)) return FALSE;
503 const uint64_t hash = dt_dev_backbuf_get_hash(&dev->pipe->backbuf);
504 if(hash == (uint64_t)-1) return FALSE;
505
506 dt_pixel_cache_entry_t *entry = NULL;
507 void *data = NULL;
508 dt_dev_pixelpipe_cache_wait_set_owner(&_darkroom_main_debug_wait, "darkroom-debug-main", dev);
509 if(!dt_dev_pixelpipe_cache_peek_gui(dev->pipe, NULL, &data, &entry,
510 &_darkroom_main_debug_wait,
511 _darkroom_debug_restart_cache_wait, dev))
512 return FALSE;
513
515
516 const int bw = (int)dev->pipe->backbuf.width;
517 const int bh = (int)dev->pipe->backbuf.height;
518 if(bw <= 0 || bh <= 0)
519 {
521 return FALSE;
522 }
523
524 const int stride = cairo_format_stride_for_width(CAIRO_FORMAT_RGB24, bw);
525 const size_t required_size = (size_t)stride * (size_t)bh;
526 const size_t entry_size = dt_pixel_cache_entry_get_size(entry);
527 if(entry_size < required_size || dt_pixel_cache_entry_get_data(entry) != data)
528 {
530 return FALSE;
531 }
532
533 cairo_surface_t *surface = cairo_image_surface_create_for_data((unsigned char *)data, CAIRO_FORMAT_RGB24,
534 bw, bh, stride);
535 if(IS_NULL_PTR(surface) || cairo_surface_status(surface) != CAIRO_STATUS_SUCCESS)
536 {
537 if(surface) cairo_surface_destroy(surface);
539 return FALSE;
540 }
541
542 float image_box[4] = { 0.0f };
544 const int wd = image_box[2];
545 const int ht = image_box[3];
546 cairo_translate(cr, image_box[0], image_box[1]);
547 if(dev->iso_12646.enabled) dt_dev_draw_iso12646_border(cr, wd, ht, border);
548 cairo_surface_set_device_scale(surface, darktable.gui->ppd, darktable.gui->ppd);
549 cairo_rectangle(cr, 0, 0, wd, ht);
550 cairo_set_source_surface(cr, surface, 0, 0);
551 cairo_fill(cr);
552
553 cairo_surface_destroy(surface);
555 return TRUE;
556}
557#endif
558
563static cairo_surface_t *_darkroom_preview_fallback_surface = NULL;
569
584
586{
591#if DARKROOM_EXPOSE_DUMB_DEBUG
592 dt_dev_pixelpipe_cache_wait_cleanup(&_darkroom_main_debug_wait, "darkroom-release-debug");
593#endif
595}
596
597static gboolean _build_preview_fallback_surface(dt_develop_t *dev, const int width, const int height, const int border,
598 const dt_aligned_pixel_t bg_color, const uint64_t zoom_hash)
599{
602 if(width <= 0 || height <= 0) return FALSE;
603
607 {
613 }
614
615 cairo_t *cr = cairo_create(_darkroom_preview_fallback_surface);
616 cairo_pattern_set_filter(cairo_get_source(cr), CAIRO_FILTER_NEAREST);
617
618 cairo_set_source_rgb(cr, bg_color[0], bg_color[1], bg_color[2]);
619 cairo_paint(cr);
620
621 const int wd = _darkroom_preview_locked.width;
622 const int ht = _darkroom_preview_locked.height;
623 const float ppd = darktable.gui->ppd;
624 const float preview_wd = wd / ppd;
625 const float preview_ht = ht / ppd;
626 const float preview_scale = dev->roi.scaling;
627 float image_box[4] = { 0.0f };
629
630 if(dev->iso_12646.enabled)
631 {
632
633
634 if(image_box[2] > 0 && image_box[3] > 0)
635 {
636 cairo_save(cr);
637 cairo_translate(cr, image_box[0], image_box[1]);
638 dt_dev_draw_iso12646_border(cr, image_box[2], image_box[3], border);
639 cairo_restore(cr);
640 }
641 }
642
644 cairo_surface_set_device_scale(_darkroom_preview_locked.surface, ppd, ppd);
645
646 // The preview surface already embeds the fit-to-window scale. To emulate the
647 // main pipe while it catches up, we only apply the additional darkroom zoom
648 // and pan around the preview image center in GUI logical coordinates.
649 const float roi_width = fminf(width, preview_wd * preview_scale);
650 const float roi_height = fminf(height, preview_ht * preview_scale);
651 const float rec_x = fmaxf(border, (width - roi_width) * 0.5f);
652 const float rec_y = fmaxf(border, (height - roi_height) * 0.5f);
653 const float rec_w = fminf(width - 2 * border, roi_width);
654 const float rec_h = fminf(height - 2 * border, roi_height);
655 cairo_rectangle(cr, rec_x, rec_y, rec_w, rec_h);
656 cairo_clip(cr);
657
658 const float tx = 0.5f * width - dev->roi.x * preview_wd * preview_scale;
659 const float ty = 0.5f * height - dev->roi.y * preview_ht * preview_scale;
660 cairo_translate(cr, tx, ty);
661 cairo_scale(cr, preview_scale, preview_scale);
662 cairo_rectangle(cr, 0, 0, preview_wd, preview_ht);
663 cairo_set_source_surface(cr, _darkroom_preview_locked.surface, 0, 0);
664 cairo_fill(cr);
666 cairo_destroy(cr);
667
671 return TRUE;
672}
673
674static gboolean _render_preview_fallback_surface(cairo_t *cr)
675{
677 cairo_set_source_surface(cr, _darkroom_preview_fallback_surface, 0, 0);
678 cairo_paint(cr);
679 return TRUE;
680}
681
682static void _paint_all(cairo_t *cri, cairo_t *cr, cairo_surface_t *image_surface)
683{
684 cairo_destroy(cr);
685 if(IS_NULL_PTR(image_surface)) return;
686 cairo_set_source_surface(cri, image_surface, 0, 0);
687 cairo_paint(cri);
688}
689
691{
698 /* Main backbuf hash for which we already asked for a retry redraw after failing
699 * to lock/render it. Used to re-queue at most once per distinct main frame so a
700 * permanently-unlockable main backbuf (e.g. evicted under memory pressure) does
701 * not spin the center redraw at frame-clock rate while we show the preview
702 * fallback. Reset whenever a main frame is rendered successfully. */
705
706static inline gboolean _darkroom_preview_fallback_valid(const dt_develop_t *dev, const int width,
707 const int height, const uint64_t zoom_hash)
708{
709 return dev
715}
716
718 const uint64_t zoom_hash)
719{
720 return state
723 && state->main_zoom_hash == zoom_hash;
724}
725
727{
728 if(IS_NULL_PTR(state)) return;
729 state->image_surface_imgid = UNKNOWN_IMAGE;
730 state->image_surface_has_main = FALSE;
731 state->image_surface_hash = DT_PIXELPIPE_CACHE_HASH_INVALID;
732 state->main_zoom_hash = 0;
733 state->pending_main_hash = DT_PIXELPIPE_CACHE_HASH_INVALID;
734}
735
736static void _darkroom_prepare_image_surface(dt_develop_t *dev, const int width, const int height,
738{
739 if(IS_NULL_PTR(dev) || !state) return;
740
741 if(state->image_surface_imgid != dev->image_storage.id
742 && state->image_surface_imgid != UNKNOWN_IMAGE)
743 {
746 }
747
748 if(state->image_surface_width == width
749 && state->image_surface_height == height
750 && !IS_NULL_PTR(dev->image_surface))
751 return;
752
753 state->image_surface_width = width;
754 state->image_surface_height = height;
755 if(dev->image_surface) cairo_surface_destroy(dev->image_surface);
756 dev->image_surface = dt_cairo_image_surface_create(CAIRO_FORMAT_RGB24, width, height);
757 state->image_surface_imgid = UNKNOWN_IMAGE;
758 state->image_surface_has_main = FALSE;
759 state->image_surface_hash = DT_PIXELPIPE_CACHE_HASH_INVALID;
761}
762
764 dt_view_t *self,
765 cairo_t *cri,
766 int32_t width,
767 int32_t height,
768 int32_t pointerx,
769 int32_t pointery)
770{
771 dt_times_t start;
772 dt_get_times(&start);
773
774 cairo_save(cri);
775
776 dt_develop_t *dev = (dt_develop_t *)self->data;
777
778 const int32_t border = dev->roi.border_size;
779
780#if DARKROOM_EXPOSE_DUMB_DEBUG
781 dt_aligned_pixel_t bg_color_dbg;
782 dt_dev_get_background_color(dev, bg_color_dbg);
783 _render_main_direct_debug(cri, dev, width, height, border, bg_color_dbg);
784 cairo_restore(cri);
785 return;
786#endif
787
788 static darkroom_expose_state_t expose_state = {
790 .image_surface_height = 0,
791 .image_surface_imgid = UNKNOWN_IMAGE,
792 .image_surface_has_main = FALSE,
793 .image_surface_hash = DT_PIXELPIPE_CACHE_HASH_INVALID,
794 .main_zoom_hash = 0,
795 .pending_main_hash = DT_PIXELPIPE_CACHE_HASH_INVALID,
796 };
797 const uint64_t zoom_hash = dt_hash(5381, (char *)&dev->roi, sizeof(dev->roi));
798 const gboolean roi_changed = !_darkroom_locked_main_valid_for_zoom(&expose_state, zoom_hash);
799
800 _darkroom_prepare_image_surface(dev, width, height, &expose_state);
801
802 cairo_t *cr = cairo_create(dev->image_surface);
803 const int full_width = dev->roi.preview_width;
804 const int full_height = dev->roi.preview_height;
805 const uint64_t main_backbuf_hash = dt_dev_backbuf_get_hash(&dev->pipe->backbuf);
806 const uint64_t preview_backbuf_hash = dt_dev_backbuf_get_hash(&dev->preview_pipe->backbuf);
807 const gboolean main_has_backbuf = main_backbuf_hash != DT_PIXELPIPE_CACHE_HASH_INVALID;
808 const gboolean preview_has_backbuf = preview_backbuf_hash != DT_PIXELPIPE_CACHE_HASH_INVALID;
809 // Compare main against the last main surface, not against the last source painted into
810 // image_surface. At fit the latter may be the preview pipe, whose different hash would make an
811 // old main backbuffer look new and briefly restore the previous zoom size.
812 const gboolean main_backbuf_is_newer
813 = main_has_backbuf && main_backbuf_hash != _darkroom_main_locked.hash;
814 const gboolean main_ready_for_current_view
815 = main_has_backbuf && (!roi_changed || main_backbuf_is_newer || !_darkroom_main_locked.surface);
816 const gboolean preview_matches_full_image
817 = preview_has_backbuf && dt_dev_pipelines_share_preview_output(dev)
818 && full_width > 0 && full_height > 0
819 && dev->preview_pipe->backbuf.width == full_width
820 && dev->preview_pipe->backbuf.height == full_height;
821 const gboolean full_image_backbuf_ready = main_ready_for_current_view || preview_matches_full_image;
822
823 dt_aligned_pixel_t bg_color = { 0.0f };
824 const char *draw_source = "background only";
825 uint64_t draw_hash = (uint64_t)-1;
826 gboolean drawn = FALSE;
827 gboolean drawn_from_main = FALSE;
828
829 dt_dev_get_background_color(dev, bg_color);
830
831 cairo_pattern_set_filter(cairo_get_source(cr), CAIRO_FILTER_NEAREST);
832
833 /* Selection policy, kept intentionally linear:
834 * 1. Prefer the main pipe whenever it already has the backbuf for the
835 * current darkroom view. We do not guess that from size. If ROI did not
836 * change, the existing main frame stays authoritative. If ROI changed, a
837 * new main backbuf hash means the updated main frame arrived.
838 * 2. If preview produced the exact same full-image buffer size first
839 * (zoom-to-fit / same ROI), treat it exactly like main.
840 * 3. Only when zoom/pan changed and main has not caught up yet, use a scaled
841 * preview fallback surface. This gives immediate ROI feedback without
842 * discarding the last valid main image for pure history changes.
843 * 4. If nothing newer is ready, keep showing the last valid composed frame to
844 * avoid flashing the background. */
845
846 /* Once a full-image backbuf exists again, the ROI-scaled preview fallback is
847 * obsolete. Keep main/preview selection simple and never let the fallback
848 * outlive a valid full-image source. */
849 if(full_image_backbuf_ready)
851
852 /* Rule 1: main wins whenever it is ready for the current view. During a
853 * zoom/pan/widget-size transition, the previous main hash stays visible until
854 * a different main hash is published; only then does main override preview
855 * fallback again. */
856 if(main_ready_for_current_view)
857 {
860 && dt_dev_render_locked_surface(cr, dev, &_darkroom_main_locked, width, height, border, bg_color))
861 {
862 expose_state.main_zoom_hash = zoom_hash;
863 expose_state.image_surface_imgid = dev->image_storage.id;
864 expose_state.image_surface_has_main = TRUE;
867 drawn = TRUE;
868 drawn_from_main = TRUE;
869 draw_source = "fresh main backbuf";
870 draw_hash = _darkroom_main_locked.hash;
871 }
872 else if(main_backbuf_is_newer && expose_state.pending_main_hash != main_backbuf_hash)
873 {
874 /* The main backbuf hash advanced but its cacheline could not be locked this
875 * frame. Ask for one retry, then remember the hash: if the same main frame
876 * stays unlockable (e.g. evicted under memory pressure), we keep presenting
877 * the preview fallback below instead of re-queueing a redraw every frame.
878 * A genuinely new main frame (different hash) re-arms the retry, and the
879 * pipeline itself re-queues a redraw when it publishes a fresh backbuf. */
880 expose_state.pending_main_hash = main_backbuf_hash;
882 }
883 }
884
885 /* Rule 2: preview is directly equivalent to main only when both pipes target
886 * the same ROI. An uncropped edit mode changes the module contract, not the
887 * current zoom: once zoomed, preview must use the scaled fallback below. */
888 if(!drawn && preview_matches_full_image)
889 {
891 "darkroom-preview", FALSE)
893 && dt_dev_render_locked_surface(cr, dev, &_darkroom_preview_locked, width, height, border, bg_color))
894 {
895 // This frame validates the composed image, not the locked main surface. Keep main_zoom_hash
896 // unchanged so the old main backbuffer cannot be reused as if it had been rendered at fit.
897 expose_state.image_surface_imgid = dev->image_storage.id;
898 expose_state.image_surface_has_main = TRUE;
900 drawn = TRUE;
901 drawn_from_main = TRUE;
902 draw_source = "fresh full-size preview backbuf";
903 draw_hash = _darkroom_preview_locked.hash;
904 }
905 }
906
907 /* Rule 3: scaled preview fallback is only for ROI changes. If the user just
908 * zoomed/panned and main has not recomputed yet, rebuild a ROI-scaled preview
909 * placeholder so navigation remains responsive. */
910 if(!drawn && roi_changed && !full_image_backbuf_ready && preview_has_backbuf)
911 {
913 "darkroom-preview", TRUE)
914 && _build_preview_fallback_surface(dev, width, height, border, bg_color, zoom_hash)
916 {
917 expose_state.image_surface_imgid = dev->image_storage.id;
918 expose_state.image_surface_has_main = FALSE;
919 drawn = TRUE;
920 drawn_from_main = FALSE;
921 draw_source = "fresh preview fallback";
923 }
924 }
925
926 /* Reuse the cached ROI-scaled preview fallback if it is still geometrically
927 * valid for this zoom/pan. This avoids rebuilding it every expose while main
928 * is still catching up. */
929 if(!drawn && roi_changed && !full_image_backbuf_ready
932 {
933 expose_state.image_surface_imgid = dev->image_storage.id;
934 expose_state.image_surface_has_main = FALSE;
935 drawn = TRUE;
936 drawn_from_main = FALSE;
937 draw_source = "reused preview fallback";
939 }
940
941 /* Rule 4: if no newer source is ready, keep the last valid main frame for
942 * the same zoom/pan. History-only changes should not glitch to preview or
943 * background while the new main backbuf is still being produced. */
944 if(!drawn && _darkroom_locked_main_valid_for_zoom(&expose_state, zoom_hash)
945 && dt_dev_render_locked_surface(cr, dev, &_darkroom_main_locked, width, height, border, bg_color))
946 {
947 expose_state.image_surface_imgid = dev->image_storage.id;
948 expose_state.image_surface_has_main = TRUE;
949 drawn = TRUE;
950 drawn_from_main = TRUE;
951 draw_source = "reused last main backbuf";
952 draw_hash = _darkroom_main_locked.hash;
953 }
954
955 if(drawn)
956 {
957 /* Persist the last composed frame so later exposes can reuse it if the
958 * next requested source is temporarily unavailable. */
959 expose_state.image_surface_has_main = drawn_from_main;
960 expose_state.image_surface_hash = draw_hash;
961 _paint_all(cri, cr, dev->image_surface);
962 dt_print(DT_DEBUG_DEV, "[darkroom] expose drew %s (backbuf hash=%" PRIu64 ")\n",
963 draw_source, draw_hash);
964 cairo_restore(cri);
965 }
966 else if(dev->image_surface && expose_state.image_surface_imgid == dev->image_storage.id)
967 {
968 /* No fresh source this time: repaint the last composed image surface
969 * rather than clearing to background and waiting for another expose. */
970 draw_source = expose_state.image_surface_has_main ? "reused last main surface"
971 : "reused last preview surface";
972 draw_hash = expose_state.image_surface_hash;
973 _paint_all(cri, cr, dev->image_surface);
974 dt_print(DT_DEBUG_DEV, "[darkroom] expose drew %s (backbuf hash=%" PRIu64 ")\n",
975 draw_source, draw_hash);
976 cairo_restore(cri);
977 }
978 else
979 {
980 /* Cold-start / no valid frame cached anywhere yet. */
981 cairo_set_source_rgb(cr, bg_color[0], bg_color[1], bg_color[2]);
982 cairo_paint(cr);
983 _paint_all(cri, cr, dev->image_surface);
984 dt_print(DT_DEBUG_DEV, "[darkroom] expose drew %s (backbuf hash=%" PRIu64 ")\n",
985 draw_source, draw_hash);
986 cairo_restore(cri);
987 }
988
989 /* check if we should create a snapshot of view */
991 {
992 /* reset the request */
994
995 /* validation of snapshot filename */
997
998 /* Store current image surface to snapshot file.
999 FIXME: add checks so that we don't make snapshots of preview pipe image surface.
1000 */
1001 const int fd = g_open(darktable.develop->proxy.snapshot.filename, O_CREAT | O_WRONLY | O_BINARY, 0600);
1002 cairo_surface_write_to_png_stream(dev->image_surface, _write_snapshot_data, GINT_TO_POINTER(fd));
1003 close(fd);
1004 }
1005
1006 // Displaying sample areas if enabled
1011 {
1012 _darkroom_pickers_draw(self, cri, width, height, pointerx, pointery, darktable.develop->color_picker.samples, FALSE);
1013 }
1014
1015 // draw guide lines if needed
1016 if(!dev->gui_module || !(dev->gui_module->flags() & IOP_FLAGS_GUIDES_SPECIAL_DRAW))
1017 {
1018 const float wd = dev->roi.preview_width;
1019 const float ht = dev->roi.preview_height;
1020 const float scaling = dt_dev_get_overlay_scale(dev);
1021
1022 cairo_save(cri);
1023 // don't draw guides on image margins
1024 dt_dev_clip_roi(dev, cri, width, height);
1025 // place origin at top-left corner of image
1026 dt_dev_rescale_roi(dev, cri, width, height);
1027
1028 // draw guides with backbuffer dimensions, positioning and scaling handled by transformations
1029 dt_guides_draw(cri, 0, 0, wd, ht, scaling);
1030 cairo_restore(cri);
1031 }
1032
1033 const gboolean picker_active = dt_iop_color_picker_is_visible(dev);
1034
1035 // draw colorpicker for in focus module or execute module callback hook
1036 // FIXME: draw picker in gui_post_expose() hook in libs/colorpicker.c -- catch would be that live samples would appear over guides, softproof/gamut text overlay would be hidden by picker
1037 if(picker_active)
1038 {
1039 GSList samples = { .data = darktable.develop->color_picker.primary_sample, .next = NULL };
1040 _darkroom_pickers_draw(self, cri, width, height, pointerx, pointery, &samples, TRUE);
1041 }
1042 else
1043 {
1044 // display mask if we have a current module activated or if the masks manager module is expanded
1045 const gboolean display_masks = (dev->gui_module && dev->gui_module->enabled)
1047
1048 if(dt_masks_get_visible_form(dev) && display_masks)
1049 dt_masks_events_post_expose(dev->gui_module, cri, width, height, pointerx, pointery);
1050
1051 // module
1052 if(dev->gui_module && dev->gui_module->enabled && dev->gui_module->gui_post_expose)
1053 dev->gui_module->gui_post_expose(dev->gui_module, cri, width, height, pointerx, pointery);
1054 }
1055
1056 // indicate if we are in gamut check or softproof mode
1058
1059 dt_show_times_f(&start, "[darkroom]", "redraw");
1060}
1061
1062void reset(dt_view_t *self)
1063{
1065}
1066
1067static void _darkroom_log_image_load_error(const int ret)
1068{
1069 switch(ret)
1070 {
1072 dt_control_log(_("Could not load the image source data."));
1073 break;
1075 dt_control_log(_("Could not read image information from the database."));
1076 break;
1077 default:
1078 dt_control_log(_("We could not load the image."));
1079 break;
1080 }
1081}
1082
1083static void _darkroom_image_loaded_callback(gpointer instance, guint request_id, guint result, gpointer user_data)
1084{
1085 dt_view_t *self = (dt_view_t *)user_data;
1086 dt_develop_t *dev = (dt_develop_t *)self->data;
1087 if(request_id == 0) return;
1088 if(darktable.view_manager->current_view != self) return;
1089
1090
1091 if(result)
1092 {
1093 _darkroom_log_image_load_error((int)result);
1094 return;
1095 }
1096
1097 darktable.develop->proxy.wb_coeffs[0] = 0.f;
1098
1099 // synch gui and flag pipe as dirty
1100 // this is done here and not in dt_read_history, as it would else be triggered before module->gui_init.
1101 // locks history mutex internally
1104
1107
1111
1112 // Clean & Init the starting point of undo/redo
1116
1117 /* signal that darktable.develop is initialized and ready to be used */
1119
1121
1122 // clear selection, we don't want selections in darkroom
1124
1125 // change active image for global actions (menu)
1128
1130
1132
1134
1136}
1137
1139{
1140 uint32_t num_selected = dt_selection_get_length(darktable.selection);
1141 int32_t imgid = dt_control_get_mouse_over_id();
1142 (void)self;
1143
1145
1146 if(imgid != UNKNOWN_IMAGE)
1147 {
1148 ; // Needed to open image from filmstrip
1149 }
1150 else if(num_selected > 1)
1151 {
1152 dt_control_log(_("The current selection contains more than one image, which is ambiguous.\n"
1153 "Select exactly one image to enter the darkroom."));
1154 return 1;
1155 }
1156 else if(num_selected == 0 && imgid == UNKNOWN_IMAGE)
1157 {
1158 dt_control_log(_("There is no image selected.\n"
1159 "Select exactly one image to enter the darkroom."));
1160 return 1;
1161 }
1162 else
1163 {
1164 // Needed to open image at startup
1166 }
1167
1169
1170 if(imgid < 0)
1171 {
1172 // fail :(
1173 dt_control_log(_("No image to open !"));
1174 return 1;
1175 }
1176
1178 return 0;
1179}
1180
1181static void _dev_change_image(dt_view_t *self, int32_t imgid)
1182{
1183 // Lazy trick to cleanup, reset, reinit, reload everything without
1184 // having to duplicate most of (but not all) the code in leave(),
1185 // try_enter() and enter() : simulate a roundtrip through lighttable.
1186 // This way, all images are loaded through the same path, handled at an higher level.
1187 // It's more robust, although slightly slower than re-initing only what is needed.
1191}
1192
1193static void _view_darkroom_filmstrip_activate_callback(gpointer instance, int32_t imgid, gpointer user_data)
1194{
1195 if(imgid > UNKNOWN_IMAGE)
1196 {
1197 // switch images in darkroom mode:
1198 _dev_change_image(user_data, imgid);
1199 }
1200}
1201
1204static void _guides_popover_preshow(gpointer user_data)
1205{
1207}
1208
1209static void _autoset_popover_preshow(gpointer user_data)
1210{
1212}
1213
1214/* overlay color */
1215static void _guides_quickbutton_clicked(GtkWidget *widget, gpointer user_data)
1216{
1217 dt_guides_button_toggled(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)));
1219}
1220
1221static void _guides_view_changed(gpointer instance, dt_view_t *old_view, dt_view_t *new_view, dt_lib_module_t *self)
1222{
1224}
1225
1233/* display: darkroom-only extras appended to the popover dt_dev_toolbox_create() builds */
1234
1241static void display_mask_checker_1_callback(GtkColorButton *widget, gpointer user_data)
1242{
1243 dt_develop_t *d = (dt_develop_t *)user_data;
1244 GdkRGBA color;
1245 gtk_color_chooser_get_rgba(GTK_COLOR_CHOOSER(widget), &color);
1246 dt_conf_set_float("plugins/darkroom/colorbalancergb/checker1/red", color.red);
1247 dt_conf_set_float("plugins/darkroom/colorbalancergb/checker1/green", color.green);
1248 dt_conf_set_float("plugins/darkroom/colorbalancergb/checker1/blue", color.blue);
1249 dt_atomic_add_int(&d->mask_preview_settings_revision, 1);
1251}
1252
1253static void display_mask_checker_2_callback(GtkColorButton *widget, gpointer user_data)
1254{
1255 dt_develop_t *d = (dt_develop_t *)user_data;
1256 GdkRGBA color;
1257 gtk_color_chooser_get_rgba(GTK_COLOR_CHOOSER(widget), &color);
1258 dt_conf_set_float("plugins/darkroom/colorbalancergb/checker2/red", color.red);
1259 dt_conf_set_float("plugins/darkroom/colorbalancergb/checker2/green", color.green);
1260 dt_conf_set_float("plugins/darkroom/colorbalancergb/checker2/blue", color.blue);
1261 dt_atomic_add_int(&d->mask_preview_settings_revision, 1);
1263}
1264
1265static void display_mask_checker_size_callback(GtkWidget *slider, gpointer user_data)
1266{
1267 dt_develop_t *d = (dt_develop_t *)user_data;
1268 dt_conf_set_int("plugins/darkroom/colorbalancergb/checker/size", (int)dt_bauhaus_slider_get(slider));
1269 dt_atomic_add_int(&d->mask_preview_settings_revision, 1);
1271}
1272
1273static void display_mask_black_and_white_callback(GtkToggleButton *toggle, gpointer user_data)
1274{
1275 dt_develop_t *d = (dt_develop_t *)user_data;
1276 dt_conf_set_bool("plugins/darkroom/colorbalancergb/mask_preview/greyscaled",
1277 gtk_toggle_button_get_active(toggle));
1278 dt_atomic_add_int(&d->mask_preview_settings_revision, 1);
1280}
1281
1282static void _darkroom_change_rendering_size(GtkWidget *combobox, gpointer user_data)
1283{
1284 dt_develop_t *d = (dt_develop_t *)user_data;
1285 dt_conf_set_int("darkroom/render_size", dt_bauhaus_combobox_get(combobox));
1287}
1288
1291#if 0
1292
1293static void _overlay_cycle_callback(dt_action_t *action)
1294{
1295 const int currentval = dt_conf_get_int("darkroom/ui/overlay_color");
1296 const int nextval = (currentval + 1) % 6; // colors can go from 0 to 5
1297 dt_conf_set_int("darkroom/ui/overlay_color", nextval);
1300}
1301
1302static void _toggle_mask_visibility_callback(dt_action_t *action)
1303{
1304 if(dt_gui_widgets_suppressed()) return;
1305
1306 dt_develop_t *dev = dt_action_view(action)->data;
1307 dt_iop_module_t *mod = dev->gui_module;
1308
1309 //retouch and spot removal module use masks differently and have different buttons associated
1310 //keep the shortcuts independent
1311 if(mod && strcmp(mod->so->op, "spots") != 0 && strcmp(mod->so->op, "retouch") != 0)
1312 {
1314
1316
1318
1320 if(grp && (grp->type & DT_MASKS_GROUP) && grp->points)
1321 {
1322 if(bd->masks_shown == DT_MASKS_EDIT_OFF)
1323 bd->masks_shown = DT_MASKS_EDIT_FULL;
1324 else
1325 bd->masks_shown = DT_MASKS_EDIT_OFF;
1326
1327 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(bd->masks_edit), bd->masks_shown != DT_MASKS_EDIT_OFF);
1328 dt_masks_set_edit_mode(mod, bd->masks_shown);
1329
1330 // set all add shape buttons to inactive
1331 for(int n = 0; n < DEVELOP_MASKS_NB_SHAPES; n++)
1332 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(bd->masks_shapes[n]), FALSE);
1333 }
1334
1336 }
1337}
1338
1339
1340#endif
1341
1342gboolean _focus_main_image(GtkAccelGroup *accel_group, GObject *accelerable, guint keyval,
1343 GdkModifierType modifier, gpointer data)
1344{
1345 gtk_widget_grab_focus(dt_ui_center(darktable.gui->ui));
1346 return TRUE;
1347}
1348
1349gboolean _switch_to_next_picture(GtkAccelGroup *accel_group, GObject *accelerable, guint keyval,
1350 GdkModifierType modifier, gpointer data)
1351{
1352 dt_view_t *view = (dt_view_t *)data;
1353 dt_develop_t *dev = (dt_develop_t *)view->data;
1354 int32_t current_img = dev->image_storage.id;
1355 GList *current_collection = dt_collection_get_all(darktable.collection, -1);
1356 GList *current_item = g_list_find(current_collection, GINT_TO_POINTER(current_img));
1357
1358 if(current_item && current_item->next)
1359 {
1360 int32_t next_img = GPOINTER_TO_INT(current_item->next->data);
1361 g_list_free(current_collection);
1362 current_collection = NULL;
1363 _dev_change_image(data, next_img);
1364 }
1365 else
1366 {
1367 g_list_free(current_collection);
1368 current_collection = NULL;
1369 }
1370
1371 return TRUE;
1372}
1373
1374gboolean _switch_to_prev_picture(GtkAccelGroup *accel_group, GObject *accelerable, guint keyval,
1375 GdkModifierType modifier, gpointer data)
1376{
1377 dt_view_t *view = (dt_view_t *)data;
1378 dt_develop_t *dev = (dt_develop_t *)view->data;
1379 int32_t current_img = dev->image_storage.id;
1380 GList *current_collection = dt_collection_get_all(darktable.collection, -1);
1381 GList *current_item = g_list_find(current_collection, GINT_TO_POINTER(current_img));
1382
1383 if(current_item && current_item->prev)
1384 {
1385 int32_t prev_img = GPOINTER_TO_INT(current_item->prev->data);
1386 g_list_free(current_collection);
1387 current_collection = NULL;
1388 _dev_change_image(data, prev_img);
1389 }
1390 else
1391 {
1392 g_list_free(current_collection);
1393 current_collection = NULL;
1394 }
1395
1396 return TRUE;
1397}
1398
1399static void _preview_pipe_finished(gpointer instance, gpointer user_data)
1400{
1401 // Get the mip size that is at most as big as our pipeline backbuf
1403 const gboolean autoset_running_before
1405 const int32_t imgid = darktable.develop->image_storage.id;
1407
1408 // Check if the cache is ready for that mipmap size.
1411 gboolean cache_ready = !IS_NULL_PTR(tmp.buf);
1413
1414 if(pipe->autoset)
1415 {
1418 }
1419
1420 const gboolean autoset_running_after
1422
1423 // While autoset iterates over modules, avoid spawning thumbnail refresh jobs on each preview completion.
1424 // We refresh once the autoset run is finished and the preview cache reached a stable state.
1425 if(cache_ready && !autoset_running_after)
1426 {
1427 const gboolean autoset_just_finished = autoset_running_before && !autoset_running_after;
1428 if(!autoset_running_before || autoset_just_finished)
1429 {
1432 }
1433 }
1434}
1435
1436/*
1437static gboolean _darkroom_toolbox_button_activate_accel(GtkAccelGroup *accel_group, GObject *accelerable,
1438 guint keyval, GdkModifierType modifier,
1439 gpointer data)
1440{
1441 GtkWidget *button = GTK_WIDGET(data);
1442 if(IS_NULL_PTR(button) || !gtk_widget_is_visible(button) || !gtk_widget_is_sensitive(button)) return FALSE;
1443
1444 gtk_button_clicked(GTK_BUTTON(button));
1445 return TRUE;
1446}
1447*/
1448
1449static void _darkroom_autoset_quickbutton_clicked(GtkButton *button, gpointer user_data)
1450{
1453 fprintf(stdout, "lauching autoset\n");
1454}
1455
1456static gchar *_darkroom_autoset_label(const dt_iop_module_t *module)
1457{
1458 gchar *clean_name = dt_history_item_get_name(module);
1459 gchar *label = g_strdup_printf("%s (%i)", clean_name, module->multi_priority);
1460 dt_free(clean_name);
1461 return label;
1462}
1463
1464static void _darkroom_autoset_module_toggled(GtkToggleButton *toggle, gpointer user_data)
1465{
1466 dt_iop_module_t *module = (dt_iop_module_t *)user_data;
1467 dt_iop_autoset_module_set_enabled(module, gtk_toggle_button_get_active(toggle));
1468}
1469
1471{
1473
1474 GList *children = gtk_container_get_children(GTK_CONTAINER(_darkroom_autoset_list));
1475 for(GList *child = children; child; child = g_list_next(child))
1476 gtk_widget_destroy(GTK_WIDGET(child->data));
1477 g_list_free(children);
1478
1479 GtkWidget *title = gtk_label_new(_("Module instances to autoset"));
1480 gtk_box_pack_start(GTK_BOX(_darkroom_autoset_list), title, FALSE, FALSE, 0);
1481 dt_gui_add_class(title, "dt_section_label");
1482
1483 for(GList *modules = g_list_last(dev->iop); modules; modules = g_list_previous(modules))
1484 {
1485 dt_iop_module_t *module = (dt_iop_module_t *)modules->data;
1486 if(IS_NULL_PTR(module->autoset)) continue;
1487
1488 gchar *label = _darkroom_autoset_label(module);
1489 GtkWidget *toggle = gtk_check_button_new_with_label(label);
1490 g_free(label);
1491
1492 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle), dt_iop_autoset_module_is_enabled(module));
1493 gtk_box_pack_start(GTK_BOX(_darkroom_autoset_list), toggle, FALSE, FALSE, 0);
1494 g_signal_connect(G_OBJECT(toggle), "toggled", G_CALLBACK(_darkroom_autoset_module_toggled), module);
1495 }
1496
1497 gtk_widget_show_all(_darkroom_autoset_list);
1498}
1499
1500static void _darkroom_autoset_popover_refresh(gpointer instance, gpointer user_data)
1501{
1503}
1504
1506{
1507 dt_develop_t *dev = (dt_develop_t *)self->data;
1508
1510 G_CALLBACK(_preview_pipe_finished), self);
1511
1512 dt_accels_new_darkroom_action(_switch_to_next_picture, self, N_("Darkroom/Actions"),
1513 N_("Switch to the next picture"), GDK_KEY_Right, GDK_MOD1_MASK, _("Triggers the action"));
1514 dt_accels_new_darkroom_action(_switch_to_prev_picture, self, N_("Darkroom/Actions"),
1515 N_("Switch to the previous picture"), GDK_KEY_Left, GDK_MOD1_MASK, _("Triggers the action"));
1516
1517 gchar *path = dt_accels_build_path(_("Darkroom/Actions"), _("Give focus to the main image"));
1519 path, dt_ui_center(darktable.gui->ui), GDK_KEY_Return, 0);
1520 dt_free(path);
1521
1522 path = dt_accels_build_path(_("Darkroom/Main image"), _("Move up"));
1524 path, dt_ui_center(darktable.gui->ui), GDK_KEY_Up, 0);
1525 dt_free(path);
1526
1527 path = dt_accels_build_path(_("Darkroom/Main image"), _("Move up (coarse step)"));
1529 path, dt_ui_center(darktable.gui->ui), GDK_KEY_Up, GDK_SHIFT_MASK);
1530 dt_free(path);
1531
1532 path = dt_accels_build_path(_("Darkroom/Main image"), _("Move up (fine step)"));
1534 path, dt_ui_center(darktable.gui->ui), GDK_KEY_Up, GDK_CONTROL_MASK);
1535 dt_free(path);
1536
1537 path = dt_accels_build_path(_("Darkroom/Main image"), _("Move down"));
1539 path, dt_ui_center(darktable.gui->ui), GDK_KEY_Down, 0);
1540 dt_free(path);
1541
1542 path = dt_accels_build_path(_("Darkroom/Main image"), _("Move down (coarse step)"));
1544 path, dt_ui_center(darktable.gui->ui), GDK_KEY_Down, GDK_SHIFT_MASK);
1545 dt_free(path);
1546
1547 path = dt_accels_build_path(_("Darkroom/Main image"), _("Move down (fine step)"));
1549 path, dt_ui_center(darktable.gui->ui), GDK_KEY_Down, GDK_CONTROL_MASK);
1550 dt_free(path);
1551
1552 path = dt_accels_build_path(_("Darkroom/Main image"), _("Move left"));
1554 path, dt_ui_center(darktable.gui->ui), GDK_KEY_Left, 0);
1555 dt_free(path);
1556
1557 path = dt_accels_build_path(_("Darkroom/Main image"), _("Move left (coarse step)"));
1559 path, dt_ui_center(darktable.gui->ui), GDK_KEY_Left, GDK_SHIFT_MASK);
1560 dt_free(path);
1561
1562 path = dt_accels_build_path(_("Darkroom/Main image"), _("Move left (fine step)"));
1564 path, dt_ui_center(darktable.gui->ui), GDK_KEY_Left, GDK_CONTROL_MASK);
1565 dt_free(path);
1566
1567 path = dt_accels_build_path(_("Darkroom/Main image"), _("Move right"));
1569 path, dt_ui_center(darktable.gui->ui), GDK_KEY_Right, 0);
1570 dt_free(path);
1571
1572 path = dt_accels_build_path(_("Darkroom/Main image"), _("Move right (coarse step)"));
1574 path, dt_ui_center(darktable.gui->ui), GDK_KEY_Right, GDK_SHIFT_MASK);
1575 dt_free(path);
1576
1577 path = dt_accels_build_path(_("Darkroom/Main image"), _("Move right (fine step)"));
1579 path, dt_ui_center(darktable.gui->ui), GDK_KEY_Right, GDK_CONTROL_MASK);
1580 dt_free(path);
1581
1582 path = dt_accels_build_path(_("Darkroom/Main image"), _("Zoom in"));
1584 path, dt_ui_center(darktable.gui->ui), GDK_KEY_plus, GDK_CONTROL_MASK);
1585 dt_free(path);
1586
1587 path = dt_accels_build_path(_("Darkroom/Main image"), _("Zoom out"));
1589 path, dt_ui_center(darktable.gui->ui), GDK_KEY_minus, GDK_CONTROL_MASK);
1590 dt_free(path);
1591 /*
1592 * Add view specific tool buttons
1593 */
1594
1595 static const dt_dev_toolbox_button_t darkroom_toolbox_buttons[]
1598 dt_dev_toolbox_create(dev, DT_VIEW_DARKROOM, darkroom_toolbox_buttons,
1599 G_N_ELEMENTS(darkroom_toolbox_buttons));
1600 dt_dev_toolbox_add_accels(dev, darktable.gui->accels->darkroom_accels, N_("Darkroom/Toolbox"),
1601 darkroom_toolbox_buttons, G_N_ELEMENTS(darkroom_toolbox_buttons));
1602
1603 /* display background options: dt_dev_toolbox_create() already built the
1604 * button and a popover with the generic controls (brightness, margins);
1605 * append darkroom-only extras (rendering size, mask preview) to that same
1606 * popover's content box. */
1607 {
1608 GtkWidget *vbox = gtk_bin_get_child(GTK_BIN(dev->display.floating_window));
1609
1610 GtkWidget *rendering;
1612 N_("Rendering size"),
1613 _("Choose at what size the main preview is rendered.\n"
1614 "Full resolution renders the pipeline at the raw original resolution.\n"
1615 "It is pixel-perfect, especially regarding denoising and deblurring, but very slow.\n"
1616 "Scaled renders at screen resolution and is the best trade-off.\n"
1617 "Pixel-level accuracy is guaranteed only when zoomed-in at 100%."),
1618 dt_conf_get_int("darkroom/render_size"),
1620 N_("full resolution (slow)"),
1621 N_("scaled (default)")
1622 );
1623 gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(rendering), TRUE, TRUE, 0);
1624
1625 gtk_box_pack_start(GTK_BOX(vbox), dt_ui_section_label_new(_("Mask preview settings")), FALSE, FALSE, 0);
1626
1627 GtkWidget *checker_1_row = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, DT_GUI_BOX_SPACING);
1628 gtk_box_pack_start(GTK_BOX(checker_1_row), dt_ui_label_new(_("Checkerboard color 1")), TRUE, TRUE, 0);
1629 GtkWidget *checker_1 = gtk_color_button_new();
1630 GdkRGBA checker_color = {
1631 .red = dt_conf_get_float("plugins/darkroom/colorbalancergb/checker1/red"),
1632 .green = dt_conf_get_float("plugins/darkroom/colorbalancergb/checker1/green"),
1633 .blue = dt_conf_get_float("plugins/darkroom/colorbalancergb/checker1/blue"),
1634 .alpha = 1.0
1635 };
1636 gtk_color_chooser_set_rgba(GTK_COLOR_CHOOSER(checker_1), &checker_color);
1637 gtk_color_chooser_set_use_alpha(GTK_COLOR_CHOOSER(checker_1), FALSE);
1638 gtk_color_button_set_title(GTK_COLOR_BUTTON(checker_1),
1639 _("Select color of the checkerboard from a swatch"));
1640 g_signal_connect(G_OBJECT(checker_1), "color-set", G_CALLBACK(display_mask_checker_1_callback), dev);
1641 gtk_box_pack_start(GTK_BOX(checker_1_row), checker_1, FALSE, FALSE, 0);
1642 gtk_box_pack_start(GTK_BOX(vbox), checker_1_row, FALSE, FALSE, 0);
1643
1644 GtkWidget *checker_2_row = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, DT_GUI_BOX_SPACING);
1645 gtk_box_pack_start(GTK_BOX(checker_2_row), dt_ui_label_new(_("Checkerboard color 2")), TRUE, TRUE, 0);
1646 GtkWidget *checker_2 = gtk_color_button_new();
1647 checker_color.red = dt_conf_get_float("plugins/darkroom/colorbalancergb/checker2/red");
1648 checker_color.green = dt_conf_get_float("plugins/darkroom/colorbalancergb/checker2/green");
1649 checker_color.blue = dt_conf_get_float("plugins/darkroom/colorbalancergb/checker2/blue");
1650 gtk_color_chooser_set_rgba(GTK_COLOR_CHOOSER(checker_2), &checker_color);
1651 gtk_color_chooser_set_use_alpha(GTK_COLOR_CHOOSER(checker_2), FALSE);
1652 gtk_color_button_set_title(GTK_COLOR_BUTTON(checker_2),
1653 _("Select color of the checkerboard from a swatch"));
1654 g_signal_connect(G_OBJECT(checker_2), "color-set", G_CALLBACK(display_mask_checker_2_callback), dev);
1655 gtk_box_pack_start(GTK_BOX(checker_2_row), checker_2, FALSE, FALSE, 0);
1656 gtk_box_pack_start(GTK_BOX(vbox), checker_2_row, FALSE, FALSE, 0);
1657
1658 GtkWidget *checker_size
1660 dt_bauhaus_slider_set(checker_size,
1661 dt_conf_get_int("plugins/darkroom/colorbalancergb/checker/size"));
1662 dt_bauhaus_slider_set_format(checker_size, " px");
1663 dt_bauhaus_widget_set_label(checker_size, _("Checkerboard size"));
1664 g_signal_connect(G_OBJECT(checker_size), "value-changed",
1665 G_CALLBACK(display_mask_checker_size_callback), dev);
1666 gtk_box_pack_start(GTK_BOX(vbox), checker_size, TRUE, TRUE, 0);
1667
1668 GtkWidget *black_and_white = gtk_check_button_new_with_label(_("Show a greyscaled mask image"));
1669 gtk_toggle_button_set_active(
1670 GTK_TOGGLE_BUTTON(black_and_white),
1671 dt_conf_get_bool("plugins/darkroom/colorbalancergb/mask_preview/greyscaled"));
1672 g_signal_connect(G_OBJECT(black_and_white), "toggled",
1673 G_CALLBACK(display_mask_black_and_white_callback), dev);
1674 gtk_box_pack_start(GTK_BOX(vbox), black_and_white, FALSE, FALSE, 0);
1675
1676 gtk_widget_show_all(vbox);
1677 }
1678
1680 gtk_widget_set_tooltip_text(_darkroom_ioporder_button, _("show the pipeline node graph"));
1681 g_signal_connect(G_OBJECT(_darkroom_ioporder_button), "clicked",
1685 N_("Darkroom/Toolbox"),
1686 N_("Show the pipeline node graph"), 0, 0,
1687 _("Triggers the action"));
1688
1689 /* create grid changer popup tool */
1690 {
1691 // the button
1693 gtk_widget_set_tooltip_text(darktable.view_manager->guides_toggle,
1694 _("toggle guide lines\nright click for guides options"));
1696 g_object_ref(darktable.view_manager->guides_popover);
1697 g_signal_connect(G_OBJECT(darktable.view_manager->guides_toggle), "clicked",
1698 G_CALLBACK(_guides_quickbutton_clicked), dev);
1701 g_object_set_data(G_OBJECT(darktable.view_manager->guides_toggle), DT_DEV_TOOLBOX_POPOVER_KEY,
1706 darktable.view_manager->guides_toggle, N_("Darkroom/Toolbox"),
1707 N_("Toggle guide lines"), 0, 0,
1708 _("Triggers the action"));
1710 darktable.view_manager->guides_toggle, N_("Darkroom/Toolbox"),
1711 N_("Focus guide lines options"), 0, 0,
1712 _("Shows the options popover"));
1713 // we want to update button state each time the view change
1715 G_CALLBACK(_guides_view_changed), dev);
1716 }
1717
1718 // Auto-set feature
1719 {
1721
1723 gtk_widget_set_tooltip_text(_darkroom_autoset_button, _("Run autoset on selected modules\nRight click for options"));
1724 g_signal_connect(G_OBJECT(_darkroom_autoset_button), "clicked",
1725 G_CALLBACK(_darkroom_autoset_quickbutton_clicked), dev);
1726 /* Ensure autoset button is placed first in the toolbox. */
1727 g_object_set_data(G_OBJECT(_darkroom_autoset_button), "dt-toolbox-priority", GINT_TO_POINTER(1));
1729
1733 g_object_set_data(G_OBJECT(_darkroom_autoset_button), DT_DEV_TOOLBOX_POPOVER_KEY,
1735 /*
1736 dt_accels_new_darkroom_action(dt_dev_toolbox_activate_accel, _darkroom_autoset_button,
1737 N_("Darkroom/Toolbox"),
1738 N_("Show the pipeline node graph"), 0, 0,
1739 _("Triggers the action"));
1740 */
1741 _darkroom_autoset_list = gtk_box_new(GTK_ORIENTATION_VERTICAL, DT_GUI_BOX_SPACING);
1742 gtk_container_add(GTK_CONTAINER(_darkroom_autoset_popover), _darkroom_autoset_list);
1745
1747 G_CALLBACK(_darkroom_autoset_popover_refresh), dev);
1749 G_CALLBACK(_darkroom_autoset_popover_refresh), dev);
1751 G_CALLBACK(_darkroom_autoset_popover_refresh), dev);
1752 }
1753
1756 dev->roi.border_size = DT_PIXEL_APPLY_DPI(dt_conf_get_int("plugins/darkroom/ui/border_size"));
1757}
1758
1760{
1761 dt_accels_t *accels = darktable.gui->accels;
1762 if(!darktable.gui->has_scroll_focus || accels->active_key.accel_key == 0) return FALSE;
1763
1764 // When declaring shortcuts, bauhaus widgets write their accel path into a private data field
1765 gchar *accel_path = g_object_get_data(G_OBJECT(darktable.gui->has_scroll_focus), "accel-path");
1766
1767 // Find if the registered accel keys matches currently pressed keys
1768 GtkAccelKey key = { 0 };
1769 return gtk_accel_map_lookup_entry(accel_path, &key)
1770 && key.accel_key == accels->active_key.accel_key
1771 && key.accel_mods == accels->active_key.accel_mods;
1772}
1773
1774
1775// If a bauhaus widget has the scroll focus from a keyboard shortcut,
1776// and the combination of keys attached to its accel path
1777// is still pressed, then we redirect any scroll event in the window to this widget.
1778// Warning: if mouse is over the central widget, central widget takes precedence over scrolling.
1779gboolean _scroll_on_focus(GdkEventScroll event, void *data)
1780{
1782 {
1783 // Pass-through the scrolling event to the scrolling handler of the widget
1784 gboolean ret;
1785 g_signal_emit_by_name(G_OBJECT(darktable.gui->has_scroll_focus), "scroll-event", &event, &ret);
1786 return ret;
1787 }
1788
1789 return FALSE;
1790}
1791
1792
1793void enter(dt_view_t *self)
1794{
1795 // Flush all background jobs (thumbnails generation) to spare resources for interactivity
1797
1798 dt_print(DT_DEBUG_CONTROL, "[run_job+] 11 %f in darkroom mode\n", dt_get_wtime());
1799 dt_develop_t *dev = (dt_develop_t *)self->data;
1800 dev->exit = 0;
1802
1803 // We need to init forms before we init module blending GUI
1804 dt_masks_gui_init(dev);
1805 dev->gui_module = NULL;
1806
1807 if(IS_NULL_PTR(dev->iop))
1808 dev->iop = dt_dev_load_modules(dev);
1809
1810 // Add IOP modules to the plugin list
1811 char option[1024];
1812 const char *active_plugin = dt_conf_get_string_const("plugins/darkroom/active");
1813 for(const GList *modules = g_list_first(dev->iop); modules; modules = g_list_next(modules))
1814 {
1815 dt_iop_module_t *module = (dt_iop_module_t *)(modules->data);
1816
1817 /* initialize gui if iop have one defined */
1818 if(!dt_iop_is_hidden(module))
1819 {
1820 dt_iop_gui_init(module);
1822
1823 if(module->multi_priority == 0)
1824 {
1825 snprintf(option, sizeof(option), "plugins/darkroom/%s/expanded", module->op);
1826 module->expanded = dt_conf_get_bool(option);
1828
1829 if(active_plugin && !strcmp(module->op, active_plugin))
1831 }
1832 }
1833 }
1834
1835 // just make sure at this stage we have only history info into the undo, all automatic
1836 // tagging should be ignored.
1838
1840
1841 // Reset focus to center view
1843
1844 // Attach shortcuts to new widgets
1848
1849 // Attach bauhaus default signal callback to IOP
1851
1852 // This gets the first selected ID to scroll where relevant, so
1853 // runs it before clearing the selection
1855 gtk_widget_show(dt_ui_center(darktable.gui->ui));
1857
1858 /* connect signal for filmstrip image activate */
1861
1862 gtk_widget_grab_focus(dt_ui_center(darktable.gui->ui)); // ensure the center view has focus for keybindings to work
1863
1864 const int32_t imgid = _darkroom_pending_imgid;
1869 int ret = dt_dev_load_image(darktable.develop, imgid);
1870 _darkroom_image_loaded_callback(NULL, imgid, ret, self);
1871}
1872
1873void leave(dt_view_t *self)
1874{
1875 dt_develop_t *dev = (dt_develop_t *)self->data;
1878 "darkroom-leave-autoset");
1884
1886 if(dev->image_surface) cairo_surface_destroy(dev->image_surface);
1887 dev->image_surface = NULL;
1888
1889 // Send all pipeline shutdown signals first
1890 dev->exit = 1;
1894 dev->pipelines_started = FALSE;
1895
1896 /* dev->exit / the shutdown atomics above are only checked by the darkroom worker thread
1897 * (dt_dev_darkroom_pipeline(), running in the DT_CTL_WORKER_DARKROOM job) between loop
1898 * iterations and between servicing each pipe -- they do not preempt it mid-flight. Below,
1899 * this function tears down dev->pipe/preview_pipe nodes, dev->iop and dev->history straight
1900 * from the GUI thread. Proceeding before that worker thread has actually returned races it:
1901 * dt_dev_pixelpipe_set_input() and the history-hash resync in _resync_pipe_with_history() touch
1902 * pipe->nodes and pipe->dev->iop without holding busy_mutex, so the worker can still be
1903 * mid-access on structures we are about to free, corrupting unrelated heap state (observed as
1904 * a crash inside an IOP module's cleanup_pipe() far away from the actual race). Block here
1905 * until the worker has fully exited both pipes' service loop. */
1907 && dt_control_running())
1908 dt_iop_nap(10000); // 10 ms
1909
1910 dt_dev_pixelpipe_cache_wait_dump_pending("darkroom-leave-before-cleanup");
1911
1912 /* Stop module-owned background threads that may still be mutating the pipe, dev or history (e.g.
1913 * drawlayer's asynchronous paint/commit worker) BEFORE we tear down pipeline nodes and history
1914 * below. Otherwise an in-flight commit resync can run a pipeline synch against pieces this function
1915 * is about to free, faulting in whatever module commits next. */
1916 for(GList *m = dev->iop; m; m = g_list_next(m))
1917 {
1918 dt_iop_module_t *mod = (dt_iop_module_t *)m->data;
1919 if(mod && mod->quiesce) mod->quiesce(mod);
1920 }
1921
1923
1924 // While we wait for possible pipelines to finish,
1925 // do the GUI cleaning.
1926
1927 // store last active plugin:
1929 dt_conf_set_string("plugins/darkroom/active", darktable.develop->gui_module->op);
1930 else
1931 dt_conf_set_string("plugins/darkroom/active", "");
1932
1933 // Hide the popover floating windows
1934 gtk_widget_hide(dev->overexposed.floating_window);
1935 gtk_widget_hide(dev->rawoverexposed.floating_window);
1936 gtk_widget_hide(dev->profile.floating_window);
1937
1938 // Detach the default callback for bauhaus widgets
1940
1941 /* disconnect from filmstrip image activate */
1943 (gpointer)self);
1944
1946
1949
1950 // Detach shortcuts
1952
1953 // Restore the previous selection
1956
1958 gtk_widget_hide(dt_ui_center(darktable.gui->ui));
1959
1960 // Pipeline nodes reference modules from dev->iop
1961 // we need to destroy objects referencing modules
1962 // before destroying the actual modules being referenced.
1968
1975
1982
1983 /* Device-side cache payloads are only an acceleration layer. Once darkroom
1984 * leaves, drop the cl_mem objects these pipes produced -- but only on the
1985 * device(s) they themselves last ran on, so we never touch cache entries
1986 * another, still-running pipe (e.g. a background thumbnail export) holds on
1987 * its own OpenCL device. */
1989 if(dev->preview_pipe->last_devid != dev->pipe->last_devid)
1991
1995
1996 // Not sure why using g_list_free_full() here shits the bed
1997 while(dev->iop)
1998 {
1999 dt_iop_module_t *module = (dt_iop_module_t *)(dev->iop->data);
2000 if(!dt_iop_is_hidden(module)) dt_iop_gui_cleanup_module(module);
2001 dt_iop_cleanup_module(module);
2002 dt_free(module);
2003 dev->iop = g_list_delete_link(dev->iop, dev->iop);
2004 }
2005 while(dev->alliop)
2006 {
2008 dt_free(dev->alliop->data);
2009 dev->alliop = g_list_delete_link(dev->alliop, dev->alliop);
2010 }
2011 dev->iop = dev->alliop = NULL;
2012
2013 // cleanup visible masks
2014 if(dev->form_gui)
2015 {
2016 dev->gui_module = NULL; // modules have already been g_free()
2018 }
2019
2020 // clear masks
2022 // dev->forms and dev->allforms are independent claims on possibly-shared objects:
2023 // release both, do not unconditionally free -- a form referenced by both only
2024 // reaches refcount 0 once (mirrors dt_dev_cleanup() in develop.c).
2025 g_list_free_full(dev->forms, (void (*)(void *))dt_masks_form_unref);
2026 dev->forms = NULL;
2027 g_list_free_full(dev->allforms, (void (*)(void *))dt_masks_form_unref);
2028 dev->allforms = NULL;
2030
2031 // Fetch the new thumbnail if needed. Ensure it runs after we save history.
2034
2035 // Release the cache entries for histogram buffers
2038
2041
2044
2045 /* GUI backbuffers were already released when each pipeline was quiesced above. Keep the view-side teardown
2046 * free of extra unref paths so pipeline ownership stays centralized. */
2047
2048
2049 dt_print(DT_DEBUG_CONTROL, "[run_job-] 11 %f in darkroom mode\n", dt_get_wtime());
2050}
2051
2052// Leave central view
2054{
2055 // if we are not hovering over a thumbnail in the filmstrip -> show metadata of opened image.
2056 dt_develop_t *dev = (dt_develop_t *)self->data;
2058 dt_gui_gtk_t *gui = darktable.gui;
2061
2063
2064 if(gui->pan_edge.timeout_source
2066 && !IS_NULL_PTR(ctl) && ctl->button_down && ctl->button_down_which == 1)
2067 {
2068 gui->pan_edge.velocity[0] = 0.0f;
2069 gui->pan_edge.velocity[1] = 0.0f;
2070 gui->pan_edge.last_time_us = 0;
2071 if(gui->pan_edge.timeout_source)
2072 {
2073 g_source_remove(gui->pan_edge.timeout_source);
2074 gui->pan_edge.timeout_source = 0;
2075 }
2076 gui->pan_edge.view = NULL;
2078 }
2079 else
2081
2082 // masks
2083 gboolean handled = FALSE;
2085 handled = TRUE;
2086 // module
2087 else if(dev->gui_module && dev->gui_module->mouse_leave
2088 && dev->gui_module->mouse_leave(dev->gui_module))
2089 handled = TRUE;
2090
2091 if(handled)
2093
2094 // reset any changes the selected plugin might have made.
2096 dt_control_change_cursor(GDK_LEFT_PTR);
2097}
2098
2099static gboolean _is_in_frame(const int width, const int height, const int x, const int y)
2100{
2101 return !((x < -DT_PIXEL_APPLY_DPI(2)) ||
2102 (x > (width + DT_PIXEL_APPLY_DPI(4))) ||
2103 (y < -DT_PIXEL_APPLY_DPI(2)) ||
2104 (y > (height + DT_PIXEL_APPLY_DPI(4))));
2105}
2106
2107/* This helper function tests for a position to be within the displayed area
2108 of an image. To avoid "border cases" we accept values to be slightly out of area too.
2109*/
2110static gboolean mouse_in_imagearea(dt_view_t *self, double x, double y)
2111{
2112 dt_develop_t *dev = (dt_develop_t *)self->data;
2113 float image_box[4] = { 0.0f };
2114 dt_dev_get_image_box_in_widget(dev, self->width, self->height, image_box);
2115
2116 return _is_in_frame(image_box[2], image_box[3], round(x - image_box[0]), round(y - image_box[1]));
2117}
2118
2119static gboolean mouse_in_actionarea(dt_view_t *self, double x, double y)
2120{
2121 return _is_in_frame(self->width, self->height, round(x), round(y));
2122}
2123
2124static void _darkroom_set_default_cursor(dt_view_t *self, double x, double y)
2125{
2126 if(mouse_in_imagearea(self, x, y))
2128 else if(mouse_in_actionarea(self, x, y))
2130 else
2132}
2133
2135{
2136 dt_develop_t *dev = (dt_develop_t *)self->data;
2138}
2139
2140static void _delayed_history_commit(gpointer data)
2141{
2142 dt_develop_t *dev = (dt_develop_t *)data;
2143
2144 // Figure out if an history item needs to be added
2145 // aka drawn masks have changed somehow. This is more expensive
2146 // but more reliable than handling individually all editing operations
2147 // in all callbacks in all possible mask types.
2148 // (dt_dev_masks_update_hash takes its own, correctly-scoped masks_mutex lock.)
2150
2151 if(dev->forms_changed)
2153}
2154
2164
2165static float _darkroom_edge_pan_velocity(const double position, const double size, const float margin)
2166{
2167 const double near_edge = position < margin ? margin - position
2168 : position > size - margin ? position - (size - margin)
2169 : 0.0;
2170 if(near_edge <= 0.0) return 0.0f;
2171
2172 const float edge_distance = CLAMPF(near_edge / margin, 0.0f, 1.0f);
2173 const float velocity = 0.10f + 0.90f * edge_distance * edge_distance;
2174 return position < margin ? -velocity : velocity;
2175}
2176
2178{
2179 dt_gui_gtk_t *gui = darktable.gui;
2180 if(IS_NULL_PTR(gui) || IS_NULL_PTR(dev)) return FALSE;
2181
2182 dt_masks_form_gui_t *form_gui = dev->form_gui;
2183 const gboolean mouse_action = gui->mouse.is_dragging && !gui->mouse.is_painting;
2184 const gboolean creating_shape_mode = !IS_NULL_PTR(form_gui) && form_gui->creation && !gui->mouse.is_painting;
2185
2186 return creating_shape_mode || mouse_action;
2187}
2188
2197 const double pointer_x,
2198 const double pointer_y,
2199 const int width,
2200 const int height,
2202{
2203 dt_gui_gtk_t *gui = darktable.gui;
2205 if(IS_NULL_PTR(gui) || IS_NULL_PTR(ctl) || IS_NULL_PTR(self)) return;
2206
2207 dt_develop_t *dev = (dt_develop_t *)self->data;
2208 if(IS_NULL_PTR(dev)) return;
2209
2210 // recheck the global eligibility conditions
2212
2213 if(!gui->pan_edge.enabled
2215 || dev->roi.scaling <= 1.0f)
2216 return;
2217
2218 float image_box[4] = { 0.0f };
2220 const double image_x = pointer_x - image_box[0];
2221 const double image_y = pointer_y - image_box[1];
2222
2223 edge->dev = dev;
2224 edge->drag = TRUE;
2225 edge->inside_image = mouse_in_imagearea(self, pointer_x, pointer_y);
2226 const gboolean inside_action_area = mouse_in_actionarea(self, pointer_x, pointer_y)
2227 && image_box[2] > 0.0f && image_box[3] > 0.0f;
2228 edge->margin = inside_action_area
2229 ? CLAMPF(MIN(image_box[2], image_box[3]) / 3, 1, DARKROOM_EDGE_PAN_MARGIN_PX)
2230 : 0.0f;
2231 edge->in_margin = inside_action_area
2232 && edge->margin > 0.0f
2233 && (image_x < edge->margin
2234 || image_x > (double)image_box[2] - edge->margin
2235 || image_y < edge->margin
2236 || image_y > (double)image_box[3] - edge->margin);
2237
2238 if(!edge->in_margin) return;
2239
2240 edge->velocity[0] = _darkroom_edge_pan_velocity(image_x, image_box[2], edge->margin);
2241 edge->velocity[1] = _darkroom_edge_pan_velocity(image_y, image_box[3], edge->margin);
2242}
2243
2252 const double pointer_x,
2253 const double pointer_y,
2254 const int width,
2255 const int height)
2256{
2257 dt_gui_gtk_t *gui = darktable.gui;
2259 darkroom_edge_pan_test_t edge = { 0 };
2260 _darkroom_edge_pan_update_state(self, pointer_x, pointer_y, width, height, &edge);
2261
2262 if(IS_NULL_PTR(gui))
2263 return FALSE;
2264
2265 // reset and exit conditions
2266 if(!gui->pan_edge.enabled || IS_NULL_PTR(self) || IS_NULL_PTR(ctl) || !edge.drag)
2267 {
2268 gui->pan_edge.timeout_source = 0;
2270 return FALSE;
2271 }
2272
2273 if(!edge.in_margin)
2274 {
2275 // Leaving the edge band stops automatic ROI motion and immediately hands
2276 // control back to the regular drag path.
2277 gui->pan_edge.timeout_source = 0;
2278 gui->pan_edge.view = self;
2279 gui->pan_edge.velocity[0] = 0.0f;
2280 gui->pan_edge.velocity[1] = 0.0f;
2281 gui->pan_edge.last_time_us = 0;
2283 ctl->button_x = pointer_x;
2284 ctl->button_y = pointer_y;
2285 return FALSE;
2286 }
2287
2288 if(gui->pan_edge.velocity[0] == 0.0f && gui->pan_edge.velocity[1] == 0.0f)
2289 {
2290 // A mouse event may have stopped edge-pan before this tick runs.
2291 // End the timeout without releasing the stored drag reference position.
2292 gui->pan_edge.timeout_source = 0;
2293 gui->pan_edge.last_time_us = 0;
2294 ctl->button_x = pointer_x;
2295 ctl->button_y = pointer_y;
2296 return FALSE;
2297 }
2298
2299 const gint64 now_us = g_get_monotonic_time();
2300 const float elapsed_s = CLAMPF((now_us - gui->pan_edge.last_time_us) / 1000000.0f, 0.001f, 0.100f);
2301 gui->pan_edge.last_time_us = now_us;
2302
2303 float delta[2] = { gui->pan_edge.velocity[0] * DARKROOM_EDGE_PAN_SPEED_PX_PER_S * elapsed_s,
2305 };
2306 dt_develop_t *dev = edge.dev;
2308
2309 float roi[2] = { dev->roi.x + delta[0] / (float)dev->roi.processed_width,
2310 dev->roi.y + delta[1] / (float)dev->roi.processed_height
2311 };
2312 dt_dev_check_zoom_pos_bounds(dev, &roi[0], &roi[1], NULL, NULL);
2313
2314 ctl->button_x = pointer_x;
2315 ctl->button_y = pointer_y;
2316
2317 if(dev->roi.x != roi[0] || dev->roi.y != roi[1])
2318 {
2319 dev->roi.x = roi[0];
2320 dev->roi.y = roi[1];
2321 // Updating ctl->button_x/y changes the cursor position, which is the same as a mouse move event.
2322 mouse_moved(self, pointer_x, pointer_y, 1.0, 0);
2323 //dt_control_queue_redraw_center();
2325 }
2326
2327 return TRUE;
2328}
2329
2336static gboolean _darkroom_edge_pan_tick(gpointer user_data)
2337{
2338 dt_gui_gtk_t *gui = darktable.gui;
2339 if(IS_NULL_PTR(gui))
2340 return FALSE;
2341
2342
2343 // Read the live pointer position instead of ctl->button_x/y: the latter only
2344 // stores the last mouse event, while the timeout must stop even if no event follows.
2345 dt_view_t *self = gui->pan_edge.view;
2347 GdkWindow *window = IS_NULL_PTR(center) ? NULL : gtk_widget_get_window(center);
2348 GdkDisplay *display = IS_NULL_PTR(window) ? NULL : gdk_window_get_display(window);
2349 GdkSeat *seat = IS_NULL_PTR(display) ? NULL : gdk_display_get_default_seat(display);
2350 GdkDevice *pointer = IS_NULL_PTR(seat) ? NULL : gdk_seat_get_pointer(seat);
2351 int pointer_x = 0;
2352 int pointer_y = 0;
2353 GtkAllocation allocation = { 0 };
2354
2355 if(IS_NULL_PTR(window) || IS_NULL_PTR(pointer))
2356 {
2357 gui->pan_edge.timeout_source = 0;
2359 return FALSE;
2360 }
2361
2362 gdk_window_get_device_position(window, pointer, &pointer_x, &pointer_y, NULL);
2363 gtk_widget_get_allocation(center, &allocation);
2364
2365 return _darkroom_edge_pan_apply(self, pointer_x, pointer_y, allocation.width, allocation.height);
2366}
2367
2368void mouse_moved(dt_view_t *self, double x, double y, double pressure, int which)
2369{
2370 dt_develop_t *dev = (dt_develop_t *)self->data;
2372 dt_gui_gtk_t *gui = darktable.gui;
2373
2374 const gboolean picker_active = dt_iop_color_picker_is_visible(dev);
2375
2376 // change cursor appearance by default
2378 gboolean handled = FALSE;
2379
2380 if(picker_active && ctl->button_down && ctl->button_down_which == 1)
2381 {
2382 // module requested a color box
2383 if(mouse_in_imagearea(self, x, y))
2384 {
2386 gboolean sample_changed = FALSE;
2387 float mouse_point[2] = { (float)x, (float)y };
2388 dt_dev_coordinates_widget_to_image_norm(dev, mouse_point, 1);
2389 const float delta[2] = {
2390 1.0f / dev->roi.processed_width,
2391 1.0f / dev->roi.processed_height
2392 };
2393
2394 if(sample->size == DT_LIB_COLORPICKER_SIZE_BOX)
2395 {
2396 float anchor[2] = { 0.0f };
2398 const float box[4] = {
2399 fmaxf(0.0, MIN(anchor[0], mouse_point[0]) - delta[0]),
2400 fmaxf(0.0, MIN(anchor[1], mouse_point[1]) - delta[1]),
2401 fminf(1.0, MAX(anchor[0], mouse_point[0]) + delta[0]),
2402 fminf(1.0, MAX(anchor[1], mouse_point[1]) + delta[1])
2403 };
2404 dt_boundingbox_t image_box = { sample->box[0], sample->box[1], sample->box[2], sample->box[3] };
2406
2407 for(int k = 0; k < 4; k++)
2408 sample_changed |= (image_box[k] != box[k]);
2409
2410 if(sample_changed)
2412 }
2413 else if(sample->size == DT_LIB_COLORPICKER_SIZE_POINT)
2414 {
2415 float image_point[2] = { 0.0f };
2416 _darkroom_sample_raw_point_to_image_norm(sample, image_point);
2417 sample_changed = memcmp(image_point, mouse_point, sizeof(mouse_point)) != 0;
2418 if(sample_changed)
2420 }
2421 }
2422 handled = TRUE;
2423 }
2424 else if(picker_active)
2425 {
2426 // Keep module-specific hover overlays and live-edit cursors disabled while the picker owns the center view.
2427 handled = TRUE;
2428 }
2429
2430 // masks
2431 else if(dt_masks_get_visible_form(dev)
2432 && dt_masks_events_mouse_moved(dev->gui_module, x, y, pressure, which))
2433 {
2434 // There is no shape dragging in creation mode, so no need to commit history.
2435 if(!dev->form_gui->creation)
2437
2438 handled = TRUE;
2439 }
2440
2441 // module
2442 else if(dev->gui_module && dev->gui_module->mouse_moved
2443 &&dev->gui_module->mouse_moved(dev->gui_module, x, y, pressure, which))
2444 {
2445 handled = TRUE;
2446 }
2447
2448 darkroom_edge_pan_test_t edge = { 0 };
2449 _darkroom_edge_pan_update_state(self, x, y, self->width, self->height, &edge);
2450
2451 if(!edge.drag && (gui->pan_edge.timeout_source || gui->pan_edge.block_normal_pan))
2452 {
2454 }
2455
2456 if(edge.drag && !edge.in_margin && gui->pan_edge.timeout_source)
2457 {
2458 /* The drag has already activated edge-pan. Leaving the edge band must stop
2459 timeout-driven ROI motion immediately and restore normal pan right away. */
2460 gui->pan_edge.view = self;
2461 gui->pan_edge.velocity[0] = 0.0f;
2462 gui->pan_edge.velocity[1] = 0.0f;
2463 gui->pan_edge.last_time_us = 0;
2465 if(gui->pan_edge.timeout_source)
2466 {
2467 g_source_remove(gui->pan_edge.timeout_source);
2468 gui->pan_edge.timeout_source = 0;
2469 }
2470 ctl->button_x = x;
2471 ctl->button_y = y;
2472 }
2473
2474 // While a left-button drag is in the edge band, the timeout owns ROI motion.
2475 // The current mouse position only updates the velocity that each tick applies.
2476 if(edge.in_margin)
2477 {
2478 gui->pan_edge.view = self;
2480 gui->pan_edge.velocity[0] = edge.velocity[0];
2481 gui->pan_edge.velocity[1] = edge.velocity[1];
2482 if(!gui->pan_edge.timeout_source)
2483 {
2484 gui->pan_edge.last_time_us = g_get_monotonic_time();
2487 }
2488 }
2489
2491
2493 && darktable.control->button_down_which == 1 && dev->roi.scaling > 1)
2494 {
2495 float delta[2] = { x - ctl->button_x, y - ctl->button_y };
2497
2498 float roi[2] = { dev->roi.x - (delta[0] / dev->roi.processed_width),
2499 dev->roi.y - (delta[1] / dev->roi.processed_height) };
2500 dt_dev_check_zoom_pos_bounds(dev, &roi[0], &roi[1], NULL, NULL);
2501
2502 dev->roi.x = roi[0];
2503 dev->roi.y = roi[1];
2504 ctl->button_x = x;
2505 ctl->button_y = y;
2506
2508 return;
2509 }
2510
2511 if(handled)
2512 {
2514 return;
2515 }
2516
2517 // Edge-pan owns ROI motion only while the timeout is actively driving updates.
2519 {
2520 ctl->button_x = x;
2521 ctl->button_y = y;
2522 return;
2523 }
2524
2525 // panning with left mouse button
2527 {
2528 float delta[2] = { x - ctl->button_x, y - ctl->button_y };
2530
2531 // new roi position in full image scale
2532 float roi[2] = { dev->roi.x - (delta[0] / dev->roi.processed_width),
2533 dev->roi.y - (delta[1] / dev->roi.processed_height) };
2534 dt_dev_check_zoom_pos_bounds(dev, &roi[0], &roi[1], NULL, NULL);
2535
2536 dev->roi.x = roi[0];
2537 dev->roi.y = roi[1];
2538
2539 // update clicked position
2540 ctl->button_x = x;
2541 ctl->button_y = y;
2542
2544 }
2545}
2546
2547
2548int button_released(dt_view_t *self, double x, double y, int which, uint32_t state)
2549{
2550 dt_develop_t *dev = (dt_develop_t *)self->data;
2551
2552 dt_print(DT_DEBUG_INPUT, "[darkroom] button released which: %d state: %d x: %.2f y: %.2f\n",
2553 which, state, x, y);
2554
2555 if(which == 1)
2556 {
2560
2562 }
2563
2564 if(dt_iop_color_picker_is_visible(dev) && which == 1)
2565 {
2566 // only sample box picker at end, for speed
2568 dt_control_queue_cursor(GDK_LEFT_PTR);
2569
2571 return 1;
2572 }
2573 // masks
2576 {
2577 // Change on mask parameters and image output.
2579 dt_dev_pixelpipe_update_history_preview(dev); // Needed if mask selection changed
2581 return 1;
2582 }
2583
2584 // module
2585 if(dev->gui_module && dev->gui_module->enabled && dev->gui_module->button_released
2586 && dev->gui_module->button_released(dev->gui_module, x, y, which, state))
2587 {
2588 // Click in modules should handle history changes internally.
2589 return 1;
2590 }
2591
2592 return 0;
2593}
2594
2595
2596int button_pressed(dt_view_t *self, double x, double y, double pressure, int which, int type, uint32_t state)
2597{
2599 dt_develop_t *dev = (dt_develop_t *)self->data;
2600
2601 dt_print(DT_DEBUG_INPUT, "[darkroom] button pressed which: %d type: %d x: %.2f y: %.2f pressure: %f\n",
2602 which, type, x, y, pressure);
2603
2604 // Grab focus on any click so we can interact from keyboard
2605 gtk_widget_grab_focus(dt_ui_center(darktable.gui->ui));
2606 if(which == 1)
2607 {
2611
2612 }
2613
2615 {
2616 float point[2] = { (float)x, (float)y };
2618
2619 const float zoom_scale = dt_dev_get_fit_scale(dev);
2620 float handle[2] = { 6.0f, 6.0f };
2622 handle[0] /= dev->roi.processed_width;
2623 handle[1] /= dev->roi.processed_height;
2624
2625 if(which == 1)
2626 {
2627 if(mouse_in_imagearea(self, x, y))
2628 {
2629 // The default box will be a square with 1% of the image width
2630 const float delta_x = 0.01f;
2631 const float delta_y = delta_x * (float)dev->roi.processed_width / (float)dev->roi.processed_height;
2632
2633 if(sample->size == DT_LIB_COLORPICKER_SIZE_BOX)
2634 {
2635 /* Box drags need one anchor corner stored in sample->point so mouse motion can stretch
2636 the rectangle against that fixed corner. Keep that anchor local to the darkroom drag
2637 state, but let the picker API own the actual sampling geometry and update signaling. */
2638 float raw_point[2] = { point[0], point[1] };
2640 memcpy(sample->point, raw_point, sizeof(raw_point));
2641
2642 dt_boundingbox_t image_box = { sample->box[0], sample->box[1], sample->box[2], sample->box[3] };
2644
2645 // this is slightly more than as drawn, to give room for slop
2646 gboolean on_corner_prev_box = TRUE;
2647 float opposite[2] = { 0.0f };
2648
2649 if(fabsf(point[0] - image_box[0]) <= handle[0])
2650 opposite[0] = image_box[2];
2651 else if(fabsf(point[0] - image_box[2]) <= handle[0])
2652 opposite[0] = image_box[0];
2653 else
2654 on_corner_prev_box = FALSE;
2655
2656 if(fabsf(point[1] - image_box[1]) <= handle[1])
2657 opposite[1] = image_box[3];
2658 else if(fabsf(point[1] - image_box[3]) <= handle[1])
2659 opposite[1] = image_box[1];
2660 else
2661 on_corner_prev_box = FALSE;
2662
2663 if(on_corner_prev_box)
2664 {
2665 float raw_opposite[2] = { opposite[0], opposite[1] };
2666 dt_dev_coordinates_image_norm_to_raw_norm(dev, raw_opposite, 1);
2667 memcpy(sample->point, raw_opposite, sizeof(raw_opposite));
2668 }
2669 else
2670 {
2671 const dt_boundingbox_t box = {
2672 fmaxf(0.0, point[0] - delta_x),
2673 fmaxf(0.0, point[1] - delta_y),
2674 fminf(1.0, point[0] + delta_x),
2675 fminf(1.0, point[1] + delta_y)
2676 };
2678 }
2679 dt_control_queue_cursor(GDK_FLEUR);
2680 }
2681 else
2682 {
2683 /* Point pickers must not pre-write sample->point before going through the picker API,
2684 otherwise the setter sees no geometry change and skips the resample request. */
2686 }
2687 }
2689 return 1;
2690 }
2691
2692 if(which == 3)
2693 {
2694 // apply a live sample's area to the active picker?
2695 // FIXME: this is a naive implementation, nicer would be to cycle through overlapping samples then reset
2698 for(GSList *samples = darktable.develop->color_picker.samples; samples; samples = g_slist_next(samples))
2699 {
2700 dt_colorpicker_sample_t *live_sample = samples->data;
2701 if(live_sample->size == DT_LIB_COLORPICKER_SIZE_BOX && picker->kind != DT_COLOR_PICKER_POINT)
2702 {
2703 dt_boundingbox_t live_box = { live_sample->box[0], live_sample->box[1], live_sample->box[2], live_sample->box[3] };
2705 if(point[0] < live_box[0] || point[0] > live_box[2]
2706 || point[1] < live_box[1] || point[1] > live_box[3])
2707 continue;
2709 }
2710 else if(live_sample->size == DT_LIB_COLORPICKER_SIZE_POINT && picker->kind != DT_COLOR_PICKER_AREA)
2711 {
2712 // magic values derived from _darkroom_pickers_draw
2713 float slop[2] = {
2714 MAX(26.0f, roundf(3.0f * zoom_scale)),
2715 MAX(26.0f, roundf(3.0f * zoom_scale))
2716 };
2718 slop[0] /= dev->roi.processed_width;
2719 slop[1] /= dev->roi.processed_height;
2720 float live_point[2] = { live_sample->point[0], live_sample->point[1] };
2722 if(fabsf(point[0] - live_point[0]) > slop[0]
2723 || fabsf(point[1] - live_point[1]) > slop[1])
2724 continue;
2726 }
2727 else
2728 continue;
2729 return 1;
2730 }
2731
2732 if(sample->size == DT_LIB_COLORPICKER_SIZE_BOX)
2733 {
2734 // default is hardcoded this way
2735 // FIXME: color_pixer_proxy should have an dt_iop_color_picker_clear_area() function for this
2736 dt_boundingbox_t reset = { 0.01f, 0.01f, 0.99f, 0.99f };
2738 }
2739 return 1;
2740 }
2741 }
2742
2743 // masks
2745 && dt_masks_events_button_pressed(dev->gui_module, x, y, pressure, which, type, state))
2746 {
2749 return 1;
2750 }
2751 // module
2752 if(dev->gui_module && dev->gui_module->enabled && dev->gui_module->button_pressed
2753 && dev->gui_module->button_pressed(dev->gui_module, x, y, pressure, which, type, state))
2754 {
2755 return 1;
2756 }
2757
2758 if(which == 1 && dev->roi.scaling > 1.0f && mouse_in_imagearea(self, x, y))
2760
2761 if(which == 2)
2762 {
2763 // Incremental zoom-in on middle button click, from fit to 800%
2764 // by power of 2 increments (100%, 200%, 400%, 800%).
2765 float new_scale = 1.f;
2766 if(dev->roi.scaling < 1.f || dev->roi.scaling > 7.f / dev->roi.natural_scale)
2767 new_scale = 1.f; // zoom to fit
2768 else if(dev->roi.scaling * dev->roi.natural_scale < 1.f)
2769 new_scale = 1.f / dev->roi.natural_scale; // 100 %
2770 else
2771 new_scale = floorf(dev->roi.scaling * dev->roi.natural_scale) * 2.f / dev->roi.natural_scale;
2772
2773 const float point[2] = { x, y };
2774 return _change_scaling(dev, point, new_scale);
2775 }
2776
2777 return 0;
2778}
2779
2780static int _change_scaling(dt_develop_t *dev, const float point[2], const float new_scaling)
2781{
2782 const float old_scaling = dev->roi.scaling;
2783
2784 // Round scaling to 1.0 (fit) if close enough
2785 const float epsilon = fabsf(old_scaling - new_scaling);
2786 if(fabsf(new_scaling - 1.0f) < epsilon)
2787 dev->roi.scaling = 1.0f;
2788 else
2789 dev->roi.scaling = new_scaling;
2790
2792 {
2793 // Calculate zoom position offset to keep mouse position fixed during zoom
2794 float center[2] = { 0.0f };
2795 float mouse_offset[2] = { point[0], point[1] };
2796 dt_dev_get_widget_center(dev, center);
2797 mouse_offset[0] -= center[0];
2798 mouse_offset[1] -= center[1];
2799
2800
2801 // Keep the image point under the mouse fixed in widget coordinates while
2802 // the pipeline zoom stays DPI-invariant.
2803 const float old_zoom = dt_dev_get_widget_zoom_scale(dev, old_scaling);
2804 const float new_zoom = dt_dev_get_widget_zoom_scale(dev, dev->roi.scaling);
2805 if(old_zoom <= 1e-6f || new_zoom <= 1e-6f)
2806 {
2807 dev->roi.scaling = old_scaling;
2808 return 0;
2809 }
2810
2811 // Adjust the center to compensate for the scale change
2812 int proc_w = 0.f, proc_h = 0.f;
2813 dt_dev_get_processed_size(dev, &proc_w, &proc_h);
2814 dev->roi.x += mouse_offset[0] * (1.f / old_zoom - 1.f / new_zoom) / proc_w;
2815 dev->roi.y += mouse_offset[1] * (1.f / old_zoom - 1.f / new_zoom) / proc_h;
2816
2817 dt_dev_check_zoom_pos_bounds(dev, &dev->roi.x, &dev->roi.y, NULL, NULL);
2819 return 1;
2820 }
2821 else
2822 {
2823 // Invalid zoom level, keep previous value
2824 dev->roi.scaling = old_scaling;
2825 return 0;
2826 }
2827}
2828
2829static gboolean _center_view_free_zoom(dt_view_t *self, double x, double y, int up, int state, int flow)
2830{
2832
2833 // Commit the new scaling
2834 const float step = 1.02f;
2835 const float new_scaling = dev->roi.scaling * powf(step, (float)-flow);
2836 const float point[2] = { x, y };
2837 return _change_scaling(dev, point, new_scaling);
2838}
2839
2840
2841int scrolled(dt_view_t *self, double x, double y, int up, int state, int delta_y)
2842{
2844
2845 dt_develop_t *dev = (dt_develop_t *)self->data;
2846
2847 dt_print(DT_DEBUG_INPUT, "[darkroom] scrolled: up: %i x: %.2f y: %.2f state: %i flow: %i\n",
2848 up, x, y, state, delta_y);
2849
2850 // masks
2852 && dt_masks_events_mouse_scrolled(dev->gui_module, x, y, up, state, delta_y))
2853 {
2855 if(!dev->form_gui->creation)
2857
2858 return TRUE;
2859 }
2860
2861 // module
2862 if(dev->gui_module && dev->gui_module->enabled && dev->gui_module->scrolled && dev->gui_module->scrolled(dev->gui_module, x, y, up, state))
2863 {
2864 // Scroll in modules should handle history changes internally.
2865 return TRUE;
2866 }
2867
2868 // free zoom
2869 return _center_view_free_zoom(self, x, y, up, state, delta_y);
2870}
2871
2872static void _key_scroll(dt_develop_t *dev)
2873{
2874 dt_dev_check_zoom_pos_bounds(dev, &dev->roi.x, &dev->roi.y, NULL, NULL);
2877}
2878
2879
2880int key_pressed(dt_view_t *self, GdkEventKey *event)
2881{
2882 if(!gtk_window_is_active(GTK_WINDOW(darktable.gui->ui->main_window))) return FALSE;
2883
2884 dt_develop_t *dev = (dt_develop_t *)self->data;
2885
2887 {
2889 return 1;
2890 }
2891
2892 // module
2893 else if(!IS_NULL_PTR(dev->gui_module)
2894 && !IS_NULL_PTR(dev->gui_module->key_pressed)
2895 && dev->gui_module->key_pressed(dev->gui_module, event))
2896 {
2898 return 1;
2899 }
2900
2901 const gboolean shift = dt_modifier_is(event->state, GDK_SHIFT_MASK);
2902 const gboolean ctrl = dt_modifier_is(event->state, GDK_CONTROL_MASK);
2903 const gboolean ctrl_any = dt_modifiers_include(event->state, GDK_CONTROL_MASK);
2904 guint key = dt_keys_mainpad_alternatives(event->keyval);
2905
2906 if(ctrl_any)
2907 {
2908 const float zoom_step = 1.1f;
2909 float center[2] = { 0.0f };
2910 dt_dev_get_widget_center(dev, center);
2911
2912 switch(key)
2913 {
2914 case GDK_KEY_plus:
2915 return _change_scaling(dev, center, dev->roi.scaling * zoom_step);
2916 case GDK_KEY_minus:
2917 return _change_scaling(dev, center, dev->roi.scaling / zoom_step);
2918 }
2919 }
2920
2921 float multiplier = (shift) ? 4.f :
2922 (ctrl) ? 0.5f :
2923 1.f;
2924
2925 float delta[2] = { 10.f * multiplier, 10.f * multiplier };
2927
2928 switch(key)
2929 {
2930 case GDK_KEY_Up:
2931 {
2932 dev->roi.y -= delta[1] / (float)dev->roi.processed_height;
2933 _key_scroll(dev);
2934 return 1;
2935 }
2936 case GDK_KEY_Down:
2937 {
2938 dev->roi.y += delta[1] / (float)dev->roi.processed_height;
2939 _key_scroll(dev);
2940 return 1;
2941 }
2942 case GDK_KEY_Left:
2943 {
2944 dev->roi.x -= delta[0] / (float)dev->roi.processed_width;
2945 _key_scroll(dev);
2946 return 1;
2947 }
2948 case GDK_KEY_Right:
2949 {
2950 dev->roi.x += delta[0] / (float)dev->roi.processed_width;
2951 _key_scroll(dev);
2952 return 1;
2953 }
2954 case GDK_KEY_Escape:
2955 {
2956 dt_ctl_switch_mode_to("lighttable");
2957 return TRUE;
2958 }
2959 }
2960
2961 return 0;
2962}
2963
2964void configure(dt_view_t *self, int wd, int ht)
2965{
2966 dt_develop_t *dev = (dt_develop_t *)self->data;
2967
2968 // Configure event is called when initing the view AND upon window resizes events (through Gtk widget/window resize commands).
2969 // At init time, final window size may not be correct just yet.
2970 // It will be when we call dt_ui_restore_panels(), which will resize stuff properly,
2971 // but that will be only when entering the current view.
2972 // Until we run dt_dev_configure(), main preview pipe gets output size -1×-1 px
2973 // which aborts the pipe recompute early. As soon as we init
2974 // sizes with something "valid" with regard to the pipe, pipeline runs.
2975 // Problem is it will not be valid with regard to the window size and the output will be thrown out
2976 // until we get the final size.
2977 // TD;DR: until we get the final window size, which happens
2978 // only when entering the view, don't configure the main preview pipeline, which will disable useless recomputes.
2980 {
2981 // Reference dimensions before ISO 12646 mode
2982 dev->roi.orig_height = ht;
2983 dev->roi.orig_width = wd;
2985 }
2986}
2987
2988// clang-format off
2989// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
2990// vim: shiftwidth=2 expandtab tabstop=2 cindent
2991// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
2992// clang-format on
void dt_accels_connect_accels(dt_accels_t *accels)
Actually enable accelerators after having loaded user config.
void dt_accels_connect_active_group(dt_accels_t *accels, const gchar *group)
Connect the contextual active accels group to the window. Views can declare their own set of contextu...
void dt_accels_disconnect_active_group(dt_accels_t *accels)
Disconnect the contextual active accels group from the window.
void dt_accels_new_virtual_shortcut(dt_accels_t *accels, GtkAccelGroup *accel_group, const gchar *accel_path, GtkWidget *widget, guint key_val, GdkModifierType accel_mods)
Add a new virtual shortcut. Virtual shortcuts are immutable, read-only and don't trigger any action....
gchar * dt_accels_build_path(const gchar *scope, const gchar *feature)
void dt_accels_attach_scroll_handler(dt_accels_t *accels, gboolean(*callback)(GdkEventScroll event, void *data), void *data)
Attach a new global scroll event callback. So far this is used in darkroom to redirect scroll events ...
void dt_accels_detach_scroll_handler(dt_accels_t *accels)
#define TRUE
Definition ashift_lsd.c:162
#define FALSE
Definition ashift_lsd.c:158
void dt_atomic_set_int(dt_atomic_int *var, int value)
int dt_atomic_get_int(dt_atomic_int *var)
int dt_atomic_add_int(dt_atomic_int *var, int incr)
int position()
#define m
Definition basecurve.c:278
float dt_bauhaus_slider_get(GtkWidget *widget)
Definition bauhaus.c:3486
int dt_bauhaus_combobox_get(GtkWidget *widget)
Definition bauhaus.c:2350
void dt_bauhaus_slider_set(GtkWidget *widget, float pos)
Definition bauhaus.c:3537
void dt_bauhaus_widget_set_label(GtkWidget *widget, const char *label)
Definition bauhaus.c:1656
GtkWidget * dt_bauhaus_slider_new_with_range(dt_bauhaus_t *bh, dt_gui_module_t *self, float min, float max, float step, float defval, int digits)
Definition bauhaus.c:1783
void dt_bauhaus_slider_set_format(GtkWidget *widget, const char *format)
Definition bauhaus.c:3613
static void set_color(cairo_t *cr, GdkRGBA color)
Definition bauhaus.h:446
#define DT_BAUHAUS_COMBOBOX_NEW_FULL(bauhaus, widget, action, label, tip, pos, callback, data,...)
Definition bauhaus.h:392
int width
Definition bilateral.h:1
int height
Definition bilateral.h:1
GtkWidget * dtgtk_button_new(DTGTKCairoPaintIconFunc paint, gint paintflags, void *paintdata)
Definition button.c:134
static const float scaling
GList * dt_collection_get_all(const dt_collection_t *collection, int limit)
Definition collection.c:889
void dt_iop_color_picker_init(void)
void dt_iop_color_picker_cleanup(void)
void dt_iop_color_picker_reset(dt_iop_module_t *module, gboolean keep)
gboolean dt_iop_color_picker_is_visible(const dt_develop_t *dev)
@ DT_COLOR_PICKER_AREA
@ DT_COLOR_PICKER_POINT
@ DT_LIB_COLORPICKER_SIZE_POINT
Definition colorpicker.h:37
@ DT_LIB_COLORPICKER_SIZE_BOX
Definition colorpicker.h:38
const float delta
typedef void((*dt_cache_allocate_t)(void *userdata, dt_cache_entry_t *entry))
void dt_image_check_camera_missing_sample(const struct dt_image_t *img)
char * key
int type
char * name
void dt_conf_set_bool(const char *name, int val)
int dt_conf_get_bool(const char *name)
void dt_conf_set_float(const char *name, float val)
float dt_conf_get_float(const char *name)
void dt_conf_set_int(const char *name, int val)
int dt_conf_get_int(const char *name)
void dt_conf_set_string(const char *name, const char *val)
const char * dt_conf_get_string_const(const char *name)
void dt_ctl_switch_mode_to(const char *mode)
Definition control.c:673
int32_t dt_control_get_mouse_over_id()
Definition control.c:939
void dt_control_mouse_is_dragging(gboolean state)
Definition control.c:423
void dt_control_set_mouse_over_id(int32_t value)
Definition control.c:947
void dt_control_log(const char *msg,...)
Definition control.c:777
void dt_control_queue_redraw_center()
request redraw of center window. This redraws the center view within a gdk critical section to preven...
Definition control.c:877
void dt_control_log_busy_leave()
Definition control.c:856
void dt_control_mouse_is_painting(gboolean state)
Definition control.c:428
void dt_control_commit_cursor()
Definition control.c:334
int dt_control_running()
Definition control.c:433
void dt_control_queue_cursor_by_name(const char *curs_str)
Queue a GTK named cursor for the next cursor commit.
Definition control.c:398
void dt_control_set_keyboard_over_id(int32_t value)
Definition control.c:973
#define dt_control_change_cursor(cursor)
Definition control.h:116
#define dt_control_queue_cursor(cursor)
Definition control.h:135
#define dt_control_set_cursor_visible(visible)
Definition control.h:148
void init(dt_view_t *self)
Definition darkroom.c:220
static dt_darkroom_layout_t _lib_darkroom_get_layout(dt_view_t *self)
Definition darkroom.c:293
static gboolean _darkroom_autoset_button_is_running
Definition darkroom.c:176
static gboolean mouse_in_actionarea(dt_view_t *self, double x, double y)
Definition darkroom.c:2119
static gboolean _darkroom_edge_pan_tick(gpointer user_data)
Move the darkroom ROI while a drag stays in the center-widget edge band.
Definition darkroom.c:2336
static void _darkroom_ioporder_quickbutton_clicked(GtkButton *button, gpointer user_data)
Definition darkroom.c:180
void leave(dt_view_t *self)
Definition darkroom.c:1873
static void _darkroom_sample_raw_point_to_image_norm(const dt_colorpicker_sample_t *const sample, float point[2])
Definition darkroom.c:303
static uint64_t _darkroom_preview_fallback_backbuf_hash
Definition darkroom.c:566
static void _darkroom_image_loaded_callback(gpointer instance, guint request_id, guint result, gpointer user_data)
Definition darkroom.c:1083
#define DARKROOM_EDGE_PAN_SPEED_PX_PER_S
Definition darkroom.c:154
static int _darkroom_preview_fallback_height
Definition darkroom.c:568
static dt_dev_pixelpipe_cache_wait_t _darkroom_preview_wait
Definition darkroom.c:562
static gboolean _is_scroll_captured_by_widget()
Definition darkroom.c:1759
int button_pressed(dt_view_t *self, double x, double y, double pressure, int which, int type, uint32_t state)
Definition darkroom.c:2596
static void _release_expose_source_caches(void)
Definition darkroom.c:585
static void _autoset_popover_preshow(gpointer user_data)
Definition darkroom.c:1209
static void _guides_quickbutton_clicked(GtkWidget *widget, gpointer user_data)
Definition darkroom.c:1215
gboolean _switch_to_prev_picture(GtkAccelGroup *accel_group, GObject *accelerable, guint keyval, GdkModifierType modifier, gpointer data)
Definition darkroom.c:1374
void cleanup(dt_view_t *self)
Definition darkroom.c:244
static dt_iop_module_t * _darkroom_pending_focus_module
Definition darkroom.c:168
#define DARKROOM_EDGE_PAN_INTERVAL_MS
Definition darkroom.c:152
static cairo_status_t _write_snapshot_data(void *closure, const unsigned char *data, unsigned int length)
Definition darkroom.c:284
static void display_mask_checker_1_callback(GtkColorButton *widget, gpointer user_data)
Persist the global mask-preview appearance and resynchronize every node using it.
Definition darkroom.c:1241
static gboolean _darkroom_is_only_selected_sample(gboolean is_primary_sample, dt_colorpicker_sample_t *selected_sample, gboolean display_samples)
Definition darkroom.c:298
static void display_mask_black_and_white_callback(GtkToggleButton *toggle, gpointer user_data)
Definition darkroom.c:1273
static gboolean mouse_in_imagearea(dt_view_t *self, double x, double y)
Definition darkroom.c:2110
static gboolean _is_in_frame(const int width, const int height, const int x, const int y)
Definition darkroom.c:2099
static int _darkroom_preview_fallback_width
Definition darkroom.c:567
void reset(dt_view_t *self)
Definition darkroom.c:1062
static gboolean _darkroom_locked_main_valid_for_zoom(const darkroom_expose_state_t *state, const uint64_t zoom_hash)
Definition darkroom.c:717
static void display_mask_checker_2_callback(GtkColorButton *widget, gpointer user_data)
Definition darkroom.c:1253
static dt_dev_locked_surface_t _darkroom_preview_locked
Definition darkroom.c:560
static gboolean _render_preview_fallback_surface(cairo_t *cr)
Definition darkroom.c:674
#define DARKROOM_EDGE_PAN_MARGIN_PX
Definition darkroom.c:153
static void _dev_change_image(dt_view_t *self, const int32_t imgid)
Definition darkroom.c:1181
static GtkWidget * _darkroom_ioporder_button
Definition darkroom.c:169
static void _darkroom_set_default_cursor(dt_view_t *self, double x, double y)
Definition darkroom.c:2124
static gboolean _darkroom_edge_pan_apply(dt_view_t *self, const double pointer_x, const double pointer_y, const int width, const int height)
Apply one edge-pan step when the current drag is still eligible.
Definition darkroom.c:2251
static void _preview_pipe_finished(gpointer instance, gpointer user_data)
Definition darkroom.c:1399
static void _darkroom_edge_pan_update_state(dt_view_t *self, const double pointer_x, const double pointer_y, const int width, const int height, darkroom_edge_pan_test_t *edge)
Test every condition that allows edge-pan for the current pointer.
Definition darkroom.c:2196
int scrolled(dt_view_t *self, double x, double y, int up, int state, int delta_y)
Definition darkroom.c:2841
static gboolean _build_preview_fallback_surface(dt_develop_t *dev, const int width, const int height, const int border, const dt_aligned_pixel_t bg_color, const uint64_t zoom_hash)
Definition darkroom.c:597
static dt_dev_locked_surface_t _darkroom_main_locked
Definition darkroom.c:559
static void _delayed_history_commit(gpointer data)
Definition darkroom.c:2140
static void _guides_popover_preshow(gpointer user_data)
Definition darkroom.c:1204
int key_pressed(dt_view_t *self, GdkEventKey *event)
Definition darkroom.c:2880
static GtkWidget * _darkroom_autoset_popover
Definition darkroom.c:174
static uint64_t _darkroom_preview_fallback_zoom_hash
Definition darkroom.c:565
static void _key_scroll(dt_develop_t *dev)
Definition darkroom.c:2872
static void _release_preview_fallback_surface(void)
Definition darkroom.c:570
gboolean _focus_main_image(GtkAccelGroup *accel_group, GObject *accelerable, guint keyval, GdkModifierType modifier, gpointer data)
Definition darkroom.c:1342
static gboolean _center_view_free_zoom(dt_view_t *self, double x, double y, int up, int state, int flow)
Definition darkroom.c:2829
void mouse_leave(dt_view_t *self)
Definition darkroom.c:2053
static void _darkroom_reset_expose_state(darkroom_expose_state_t *state)
Definition darkroom.c:726
void mouse_moved(dt_view_t *self, double x, double y, double pressure, int which)
Definition darkroom.c:2368
void configure(dt_view_t *self, int wd, int ht)
Definition darkroom.c:2964
uint32_t view(const dt_view_t *self)
Definition darkroom.c:228
static void _darkroom_log_image_load_error(const int ret)
Definition darkroom.c:1067
int button_released(dt_view_t *self, double x, double y, int which, uint32_t state)
Definition darkroom.c:2548
static void _view_darkroom_filmstrip_activate_callback(gpointer instance, int32_t imgid, gpointer user_data)
Definition darkroom.c:1193
static dt_autoset_manager_t * _autoset_manager
Definition darkroom.c:172
static void _reset_edge_pan()
Definition darkroom.c:233
static void _guides_view_changed(gpointer instance, dt_view_t *old_view, dt_view_t *new_view, dt_lib_module_t *self)
Definition darkroom.c:1221
void enter(dt_view_t *self)
Definition darkroom.c:1793
static void _darkroom_change_rendering_size(GtkWidget *combobox, gpointer user_data)
Definition darkroom.c:1282
static cairo_surface_t * _darkroom_preview_fallback_surface
Definition darkroom.c:563
static void display_mask_checker_size_callback(GtkWidget *slider, gpointer user_data)
Definition darkroom.c:1265
static float _darkroom_edge_pan_velocity(const double position, const double size, const float margin)
Definition darkroom.c:2165
static GtkWidget * _darkroom_autoset_list
Definition darkroom.c:175
static void _darkroom_autoset_popover_rebuild(dt_develop_t *dev)
Definition darkroom.c:1470
static void _darkroom_autoset_quickbutton_clicked(GtkButton *button, gpointer user_data)
Definition darkroom.c:1449
static dt_dev_pixelpipe_cache_wait_t _darkroom_main_wait
Definition darkroom.c:561
void expose(dt_view_t *self, cairo_t *cri, int32_t width, int32_t height, int32_t pointerx, int32_t pointery)
Definition darkroom.c:763
static void _darkroom_sample_raw_box_to_image_norm(const dt_colorpicker_sample_t *const sample, float box[4])
Definition darkroom.c:311
static int _change_scaling(dt_develop_t *dev, const float point[2], const float new_scaling)
Definition darkroom.c:2780
gboolean _switch_to_next_picture(GtkAccelGroup *accel_group, GObject *accelerable, guint keyval, GdkModifierType modifier, gpointer data)
Definition darkroom.c:1349
static gchar * _darkroom_autoset_label(const dt_iop_module_t *module)
Definition darkroom.c:1456
static int32_t _darkroom_preview_fallback_imgid
Definition darkroom.c:564
static gboolean _darkroom_edge_pan_enable_check(dt_develop_t *dev)
Definition darkroom.c:2177
static void _paint_all(cairo_t *cri, cairo_t *cr, cairo_surface_t *image_surface)
Definition darkroom.c:682
static void _darkroom_autoset_popover_refresh(gpointer instance, gpointer user_data)
Definition darkroom.c:1500
void gui_init(dt_view_t *self)
Definition darkroom.c:1505
static void _darkroom_autoset_button_set_running(const gboolean running)
Reflect autoset processing state on the darkroom quick button.
Definition darkroom.c:196
static GtkWidget * _darkroom_autoset_button
Definition darkroom.c:173
gboolean _scroll_on_focus(GdkEventScroll event, void *data)
Definition darkroom.c:1779
int try_enter(dt_view_t *self)
Definition darkroom.c:1138
void mouse_enter(dt_view_t *self)
Definition darkroom.c:2134
static int32_t _darkroom_pending_imgid
Definition darkroom.c:167
static void _darkroom_autoset_module_toggled(GtkToggleButton *toggle, gpointer user_data)
Definition darkroom.c:1464
static gboolean _darkroom_center_pan_drag
Definition darkroom.c:170
static void _darkroom_prepare_image_surface(dt_develop_t *dev, const int width, const int height, darkroom_expose_state_t *state)
Definition darkroom.c:736
static void _darkroom_pickers_draw(dt_view_t *self, cairo_t *cri, int32_t width, int32_t height, int32_t pozx, int32_t pozy, GSList *samples, gboolean is_primary_sample)
Draw colorpicker samples overlays in darkroom view.
Definition darkroom.c:330
static gboolean _darkroom_preview_fallback_valid(const dt_develop_t *dev, const int width, const int height, const uint64_t zoom_hash)
Definition darkroom.c:706
darktable_t darktable
Definition darktable.c:183
void dt_show_times_f(const dt_times_t *start, const char *prefix, const char *suffix,...)
Definition darktable.c:1652
void dt_print(dt_debug_thread_t thread, const char *msg,...)
Definition darktable.c:1600
#define dt_free_align(ptr)
Definition darktable.h:503
static void * dt_calloc_align(size_t size)
Definition darktable.h:510
static gboolean dt_modifiers_include(const GdkModifierType state, const GdkModifierType desired_modifier_mask)
Definition darktable.h:963
#define UNKNOWN_IMAGE
Definition darktable.h:194
@ DT_DEBUG_INPUT
Definition darktable.h:751
@ DT_DEBUG_CONTROL
Definition darktable.h:738
@ DT_DEBUG_DEV
Definition darktable.h:739
#define dt_gui_freeze_begin()
Definition darktable.h:900
#define O_BINARY
Definition darktable.h:77
#define dt_gui_freeze_end()
Definition darktable.h:901
float dt_boundingbox_t[4]
Definition darktable.h:731
#define DT_MODULE(MODVER)
Definition darktable.h:140
#define dt_free(ptr)
Definition darktable.h:478
void void void gboolean dt_gui_widgets_suppressed(void)
Definition gtk.c:194
static void dt_get_times(dt_times_t *t)
Definition darktable.h:983
static uint64_t dt_hash(uint64_t hash, const char *str, size_t size)
Definition darktable.h:1123
static double dt_get_wtime(void)
Definition darktable.h:976
static gboolean dt_modifier_is(const GdkModifierType state, const GdkModifierType desired_modifier_mask)
Definition darktable.h:955
#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
void dt_dev_draw_iso12646_border(cairo_t *cr, double width, double height, int border)
Definition dev_backbuf.c:53
gboolean dt_dev_lock_pipe_surface(dt_develop_t *dev, dt_dev_pixelpipe_t *pipe, dt_dev_locked_surface_t *locked, dt_dev_pixelpipe_cache_wait_t *wait, const char *wait_owner_tag, gboolean keep_previous_on_fail)
gboolean dt_dev_render_locked_surface(cairo_t *cr, const dt_develop_t *dev, dt_dev_locked_surface_t *locked, const int width, const int height, const int border, const dt_aligned_pixel_t bg_color)
void dt_dev_get_background_color(const dt_develop_t *dev, dt_aligned_pixel_t bg_color)
Definition dev_backbuf.c:44
void dt_dev_release_locked_surface(dt_dev_locked_surface_t *locked)
Definition dev_backbuf.c:87
void dt_dev_draw_profile_mode_label(cairo_t *cri, int height)
Definition dev_backbuf.c:61
void dt_dev_history_free_history(dt_develop_t *dev)
Free the whole history list attached to dev->history.
void dt_dev_pop_history_items(dt_develop_t *dev)
Thread-safe wrapper around dt_dev_pop_history_items_ext(), then update GUI.
void dt_dev_history_gui_update(dt_develop_t *dev)
Apply history-loaded params to module GUIs.
#define dt_dev_add_history_item(dev, module, enable, redraw)
void dt_dev_pixelpipe_cache_wait_cleanup(dt_dev_pixelpipe_cache_wait_t *wait, const char *reason)
Cancel one pending GUI cache wait request and clear its runtime state.
gboolean dt_dev_pixelpipe_is_backbufer_valid(dt_dev_pixelpipe_t *pipe)
void dt_dev_pixelpipe_cache_wait_dump_pending(const char *reason)
Dump pending GUI cache wait requests for lifecycle debugging.
void dt_dev_pixelpipe_change_zoom_main(dt_develop_t *dev)
void dt_dev_pixelpipe_cache_wait_set_owner(dt_dev_pixelpipe_cache_wait_t *wait, const char *owner_tag, gpointer owner_object)
Attach debug ownership metadata to one cache wait request.
gboolean dt_dev_pixelpipe_cache_peek_gui(dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece, void **data, dt_pixel_cache_entry_t **cache_entry, dt_dev_pixelpipe_cache_wait_t *wait, dt_dev_pixelpipe_cache_ready_callback_t restart, gpointer restart_data)
#define dt_dev_pixelpipe_rebuild_all(dev)
#define dt_dev_pixelpipe_resync_history_main(dev)
#define dt_dev_pixelpipe_update_history_preview(dev)
gboolean dt_dev_toolbox_focus_accel(GtkAccelGroup *accel_group, GObject *accelerable, guint keyval, GdkModifierType modifier, gpointer data)
void dt_dev_toolbox_connect_popover(GtkWidget *button, GtkWidget *popover)
gboolean dt_dev_toolbox_activate_accel(GtkAccelGroup *accel_group, GObject *accelerable, guint keyval, GdkModifierType modifier, gpointer data)
void dt_dev_toolbox_popover_set_preshow(GtkWidget *popover, void(*preshow)(gpointer user_data), gpointer user_data)
Definition dev_toolbox.c:66
void dt_dev_toolbox_create(dt_develop_t *dev, dt_view_type_flags_t views, const dt_dev_toolbox_button_t *buttons, size_t n_buttons)
void dt_dev_toolbox_apply_iso_12646_size(dt_develop_t *dev)
Definition dev_toolbox.c:38
void dt_dev_toolbox_add_accels(dt_develop_t *dev, GtkAccelGroup *accel_group, const char *category, const dt_dev_toolbox_button_t *buttons, size_t n_buttons)
dt_dev_toolbox_button_t
Definition dev_toolbox.h:27
@ DT_DEV_TOOLBOX_DISPLAY
Definition dev_toolbox.h:33
@ DT_DEV_TOOLBOX_GAMUT
Definition dev_toolbox.h:32
@ DT_DEV_TOOLBOX_RAWOVEREXPOSED
Definition dev_toolbox.h:30
@ DT_DEV_TOOLBOX_OVEREXPOSED
Definition dev_toolbox.h:29
@ DT_DEV_TOOLBOX_SOFTPROOF
Definition dev_toolbox.h:31
@ DT_DEV_TOOLBOX_ISO_12646
Definition dev_toolbox.h:28
#define DT_DEV_TOOLBOX_POPOVER_KEY
void dt_dev_get_processed_size(const dt_develop_t *dev, int *procw, int *proch)
Definition develop.c:1032
int dt_dev_get_thumbnail_size(dt_develop_t *dev)
Definition develop.c:356
void dt_dev_get_image_box_in_widget(const dt_develop_t *dev, const int32_t width, const int32_t height, float *box)
Get the displayed image rectangle in darkroom widget coordinates.
Definition develop.c:1801
void dt_dev_get_widget_center(const dt_develop_t *dev, float *point)
Get the center of the darkroom widget in logical coordinates.
Definition develop.c:1794
void dt_dev_cleanup(dt_develop_t *dev)
Definition develop.c:225
float dt_dev_get_overlay_scale(dt_develop_t *dev)
Get the overlay scale factor in GUI logical coordinates.
Definition develop.c:1783
dt_dev_image_storage_t dt_dev_load_image(dt_develop_t *dev, const int32_t imgid)
Definition develop.c:940
void dt_dev_init(dt_develop_t *dev, int32_t gui_attached)
Definition develop.c:128
void dt_dev_undo_start_record(dt_develop_t *dev)
Definition develop.c:1678
void dt_dev_coordinates_raw_norm_to_image_norm(dt_develop_t *dev, float *points, size_t num_points)
Definition develop.c:1184
void dt_dev_check_zoom_pos_bounds(dt_develop_t *dev, float *dev_x, float *dev_y, float *box_w, float *box_h)
Ensure that the current ROI position is within allowed bounds .
Definition develop.c:997
gboolean dt_dev_check_zoom_scale_bounds(dt_develop_t *dev)
Ensure that the current zoom level is within allowed bounds (for scrolling).
Definition develop.c:1915
void dt_dev_reset_roi(dt_develop_t *dev)
Definition develop.c:1822
GList * dt_dev_load_modules(dt_develop_t *dev)
Definition develop.c:98
void dt_dev_set_backbuf(dt_backbuf_t *backbuf, const int width, const int height, const size_t bpp, const int64_t hash, const int64_t history_hash)
Definition develop.c:1957
float dt_dev_get_widget_zoom_scale(const dt_develop_t *dev, const float scaling)
Convert a darkroom scaling factor to GUI logical zoom.
Definition develop.c:1788
void dt_dev_coordinates_image_norm_to_raw_norm(dt_develop_t *dev, float *points, size_t num_points)
Definition develop.c:1177
gboolean dt_dev_pipelines_share_preview_output(dt_develop_t *dev)
Tell whether the darkroom main and preview pipes currently target the same GUI output.
Definition develop.c:504
void dt_dev_masks_update_hash(dt_develop_t *dev)
Definition develop.c:1745
gboolean dt_dev_clip_roi(dt_develop_t *dev, cairo_t *cr, int32_t width, int32_t height)
Clip the view to the ROI. WARNING: this must be done before any translation.
Definition develop.c:1852
void dt_dev_undo_end_record(dt_develop_t *dev)
Definition develop.c:1689
gboolean dt_dev_rescale_roi(dt_develop_t *dev, cairo_t *cr, int32_t width, int32_t height)
Scale the ROI to fit within given width/height, centered.
Definition develop.c:1895
void dt_dev_coordinates_widget_delta_to_image_delta(dt_develop_t *dev, float *points, size_t num_points)
Convert a widget-space distance to processed-image pixels.
Definition develop.c:1039
float dt_dev_get_fit_scale(dt_develop_t *dev)
Get the scale factor that maps preview-buffer pixels to GUI coordinates.
Definition develop.c:1777
void dt_dev_coordinates_widget_to_image_norm(dt_develop_t *dev, float *points, size_t num_points)
Coordinate conversion helpers between widget, normalized image, and absolute image spaces.
Definition develop.c:1056
gchar * dt_history_item_get_name(const struct dt_iop_module_t *module)
Definition develop.c:1557
void dt_dev_start_all_pipelines(dt_develop_t *dev)
Definition develop.c:864
@ DT_DEV_IMAGE_STORAGE_DB_NOT_READ
Definition develop.h:532
@ DT_DEV_IMAGE_STORAGE_MIPMAP_NOT_FOUND
Definition develop.h:531
void dtgtk_cairo_paint_grid(cairo_t *cr, gint x, gint y, gint w, gint h, gint flags, void *data)
void dtgtk_cairo_paint_flowchart(cairo_t *cr, gint x, gint y, gint w, gint h, gint flags, void *data)
Paint the flowchart icon using normalized 0..1 coordinates.
void dtgtk_cairo_paint_wand(cairo_t *cr, gint x, gint y, gint w, gint h, gint flags, void *data)
static int dt_pthread_rwlock_unlock(dt_pthread_rwlock_t *rwlock)
Definition dtpthread.h:452
static int dt_pthread_mutex_unlock(dt_pthread_mutex_t *mutex) RELEASE(mutex) NO_THREAD_SAFETY_ANALYSIS
Definition dtpthread.h:384
static int dt_pthread_mutex_lock(dt_pthread_mutex_t *mutex) ACQUIRE(mutex) NO_THREAD_SAFETY_ANALYSIS
Definition dtpthread.h:374
static int dt_pthread_rwlock_wrlock(dt_pthread_rwlock_t *rwlock)
Definition dtpthread.h:534
static guint dt_keys_mainpad_alternatives(const guint key_val)
Remap keypad keys to usual mainpad ones.
Definition gdkkeys.h:113
void dt_gui_remove_class(GtkWidget *widget, const gchar *class_name)
Definition gtk.c:143
void dt_gui_refocus_center()
Definition gtk.c:3482
void dt_gui_add_class(GtkWidget *widget, const gchar *class_name)
Definition gtk.c:133
GtkWidget * dt_ui_center(dt_ui_t *ui)
get the center drawable widget
static cairo_surface_t * dt_cairo_image_surface_create(cairo_format_t format, int width, int height)
Definition gtk.h:322
#define dt_accels_new_darkroom_action(a, b, c, d, e, f, g)
Definition gtk.h:448
static GtkWidget * dt_ui_section_label_new(const gchar *str)
Definition gtk.h:469
#define DT_GUI_BOX_SPACING
Definition gtk.h:109
#define DT_PIXEL_APPLY_DPI(value)
Definition gtk.h:90
static GtkWidget * dt_ui_label_new(const gchar *str)
Definition gtk.h:479
#define DT_GUI_MODULE(x)
void dt_gui_throttle_cancel(gpointer source)
void dt_gui_throttle_queue(gpointer source, dt_gui_throttle_callback_t callback, gpointer user_data)
void dt_guides_draw(cairo_t *cr, const float left, const float top, const float width, const float height, const float zoom_scale)
Definition guides.c:783
void dt_guides_update_popover_values()
Definition guides.c:847
void dt_guides_set_overlay_colors()
Definition guides.c:666
GtkWidget * dt_guides_popover(dt_view_t *self, GtkWidget *button)
Definition guides.c:703
void dt_guides_button_toggled(gboolean active)
Definition guides.c:776
void dt_guides_update_button_state()
Definition guides.c:766
void dt_iop_gui_cleanup_module(dt_iop_module_t *module)
Definition imageop.c:2036
void dt_iop_gui_set_expander(dt_iop_module_t *module)
Definition imageop.c:2793
void dt_iop_gui_init(dt_iop_module_t *module)
Definition imageop.c:1235
void dt_iop_cleanup_module(dt_iop_module_t *module)
Definition imageop.c:1578
void dt_iop_nap(int32_t usec)
Definition imageop.c:2973
void dt_iop_gui_update_expanded(dt_iop_module_t *module)
Definition imageop.c:2398
void dt_iop_request_focus(dt_iop_module_t *module)
Definition imageop.c:2221
gboolean dt_iop_is_hidden(dt_iop_module_t *module)
Definition imageop.c:1133
void dt_bauhaus_value_changed_default_callback(GtkWidget *widget)
Definition imageop.c:3305
@ IOP_FLAGS_GUIDES_SPECIAL_DRAW
Definition imageop.h:208
int dt_iop_autoset_advance(struct dt_develop_t *dev, dt_autoset_manager_t *manager)
gboolean dt_iop_autoset_module_is_enabled(const dt_iop_module_t *module)
Definition iop-autoset.c:88
void dt_iop_autoset_build_list(struct dt_develop_t *dev, dt_autoset_manager_t *manager)
void dt_iop_autoset_module_set_enabled(const dt_iop_module_t *module, const gboolean enabled)
Definition iop-autoset.c:98
static const float x
void dt_control_flush_jobs_queue(dt_control_t *control, dt_job_queue_t queue_id)
Definition jobs.c:384
@ DT_JOB_QUEUE_SYSTEM_FG
Definition jobs.h:54
void dt_lib_colorpicker_set_box_area(dt_lib_t *lib, const dt_boundingbox_t box)
Definition lib.c:1456
gboolean dt_lib_gui_get_expanded(dt_lib_module_t *module)
Definition lib.c:1119
void dt_lib_colorpicker_set_point(dt_lib_t *lib, const float pos[2])
Definition lib.c:1478
dt_lib_module_t * dt_lib_get_module(const char *name)
Definition lib.c:1499
float *const restrict const size_t k
void dt_masks_events_post_expose(struct dt_iop_module_t *module, cairo_t *cr, int32_t width, int32_t height, int32_t pointerx, int32_t pointery)
int dt_masks_events_key_pressed(struct dt_iop_module_t *module, GdkEventKey *event)
@ DT_MASKS_EDIT_OFF
Definition masks.h:202
@ DT_MASKS_EDIT_FULL
Definition masks.h:203
void dt_masks_gui_init(struct dt_develop_t *dev)
int dt_masks_events_button_released(struct dt_iop_module_t *module, double x, double y, int which, uint32_t state)
int dt_masks_events_mouse_moved(struct dt_iop_module_t *module, double x, double y, double pressure, int which)
int dt_masks_events_mouse_scrolled(struct dt_iop_module_t *module, double x, double y, int up, uint32_t state, int delta_y)
@ DT_MASKS_GROUP
Definition masks.h:134
int dt_masks_events_mouse_leave(struct dt_iop_module_t *module)
int dt_masks_events_button_pressed(struct dt_iop_module_t *module, double x, double y, double pressure, int which, int type, uint32_t state)
int dt_masks_events_mouse_enter(struct dt_iop_module_t *module)
dt_masks_form_t * dt_masks_get_from_id(dt_develop_t *dev, int id)
#define DEVELOP_MASKS_NB_SHAPES
Definition masks.h:937
void dt_masks_gui_cleanup(struct dt_develop_t *dev)
dt_masks_form_t * dt_masks_get_visible_form(const struct dt_develop_t *dev)
void dt_masks_set_edit_mode(struct dt_iop_module_t *module, dt_masks_edit_mode_t value)
void dt_masks_form_unref(dt_masks_form_t *form)
#define CLAMPF(a, mn, mx)
Definition math.h:89
#define M_PI
Definition math.h:45
size_t size
Definition mipmap_cache.c:3
dt_mipmap_size_t dt_mipmap_cache_get_fitting_size(const dt_mipmap_cache_t *cache, const int32_t width, const int32_t height, const uint32_t imgid)
#define dt_mipmap_cache_get(A, B, C, D, E, F)
@ DT_MIPMAP_TESTLOCK
#define dt_mipmap_cache_release(A, B)
dt_mipmap_size_t
float dt_aligned_pixel_t[4]
void * dt_pixel_cache_entry_get_data(dt_pixel_cache_entry_t *entry)
void dt_dev_pixelpipe_cache_unref_hash(dt_dev_pixelpipe_cache_t *cache, const uint64_t hash)
Find the entry matching hash, and decrease its ref_count if found.
void dt_dev_pixelpipe_cache_flush_clmem_for_pipe(dt_dev_pixelpipe_cache_t *cache, const int devid)
Like dt_dev_pixelpipe_cache_flush_clmem(), for callers that do not hold darktable....
size_t dt_pixel_cache_entry_get_size(dt_pixel_cache_entry_t *entry)
Peek the size (in bytes) reserved for the host buffer of a cache entry.
void dt_dev_pixelpipe_cache_rdlock_entry(dt_dev_pixelpipe_cache_t *cache, gboolean lock, dt_pixel_cache_entry_t *cache_entry)
Lock or release the read lock on the entry.
#define DT_PIXELPIPE_CACHE_HASH_INVALID
void dt_dev_pixelpipe_cleanup_nodes(dt_dev_pixelpipe_t *pipe)
static void dt_dev_backbuf_set_hash(dt_backbuf_t *backbuf, const uint64_t hash)
static uint64_t dt_dev_backbuf_get_hash(const dt_backbuf_t *backbuf)
#define lw
Definition retouch.c:1055
int32_t dt_selection_get_first_id(struct dt_selection_t *selection)
Definition selection.c:69
int dt_selection_get_length(struct dt_selection_t *selection)
Definition selection.c:179
void dt_selection_clear(dt_selection_t *selection)
Definition selection.c:266
void dt_selection_select_single(dt_selection_t *selection, int32_t imgid)
Definition selection.c:289
#define DT_DEBUG_CONTROL_SIGNAL_DISCONNECT(ctlsig, cb, user_data)
Definition signal.h:387
#define DT_DEBUG_CONTROL_SIGNAL_RAISE(ctlsig, signal,...)
Definition signal.h:366
@ DT_SIGNAL_DEVELOP_INITIALIZE
This signal is raised when darktable.develop is initialized.
Definition signal.h:169
@ DT_SIGNAL_VIEWMANAGER_THUMBTABLE_ACTIVATE
Definition signal.h:95
@ DT_SIGNAL_DEVELOP_HISTORY_CHANGE
This signal is raised when develop history is changed no param, no returned value.
Definition signal.h:204
@ DT_SIGNAL_DEVELOP_IMAGE_CHANGED
This signal is raised when image is changed in darkroom.
Definition signal.h:221
@ 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
@ DT_SIGNAL_DEVELOP_MODULE_REMOVE
This signal is raised when a module is removed from the history stack 1 module no returned value.
Definition signal.h:215
@ DT_SIGNAL_VIEWMANAGER_VIEW_CHANGED
This signal is raised by viewmanager when a view has changed. 1 : dt_view_t * the old view 2 : dt_vie...
Definition signal.h:81
#define DT_DEBUG_CONTROL_SIGNAL_CONNECT(ctlsig, signal, cb, user_data)
Definition signal.h:376
struct _GtkWidget GtkWidget
Definition splash.h:29
const float uint32_t state[4]
unsigned __int64 uint64_t
Definition strptime.c:75
uint64_t image_surface_hash
Definition darkroom.c:696
gboolean image_surface_has_main
Definition darkroom.c:695
struct dt_undo_t * undo
Definition darktable.h:815
struct dt_lib_t * lib
Definition darktable.h:799
struct dt_dev_pixelpipe_cache_t * pixelpipe_cache
Definition darktable.h:818
struct dt_gui_gtk_t * gui
Definition darktable.h:803
struct dt_collection_t * collection
Definition darktable.h:809
struct dt_mipmap_cache_t * mipmap_cache
Definition darktable.h:804
struct dt_selection_t * selection
Definition darktable.h:810
struct dt_control_signal_t * signals
Definition darktable.h:802
struct dt_bauhaus_t * bauhaus
Definition darktable.h:806
struct dt_develop_t * develop
Definition darktable.h:798
struct dt_view_manager_t * view_manager
Definition darktable.h:800
struct dt_control_t * control
Definition darktable.h:801
GtkAccelKey active_key
GtkAccelGroup * darkroom_accels
gboolean progress_cursor_active
Definition iop-autoset.h:24
void(* default_value_changed_callback)(GtkWidget *widget)
Definition bauhaus.h:288
dt_lib_colorpicker_size_t size
Definition colorpicker.h:63
dt_boundingbox_t box
Definition colorpicker.h:62
int button_down_which
Definition control.h:220
double button_y
Definition control.h:221
double button_x
Definition control.h:221
int button_down
Definition control.h:220
struct dt_pixel_cache_entry_t * entry
Definition dev_backbuf.h:43
cairo_surface_t * surface
Definition dev_backbuf.h:44
dt_pthread_mutex_t busy_mutex
dt_backbuf_t backbuf
dt_atomic_int shutdown
dt_atomic_int running
int32_t gui_attached
Definition develop.h:162
cairo_surface_t * image_surface
Definition develop.h:505
struct dt_develop_t::@25 profile
dt_image_t image_storage
Definition develop.h:259
struct dt_develop_t::@19 color_picker
Authoritative darkroom color-picker state.
struct dt_develop_t::@23 display
int32_t orig_height
Definition develop.h:205
struct dt_colorpicker_sample_t * primary_sample
Definition develop.h:397
int32_t border_size
Definition develop.h:200
dt_backbuf_t display_histogram
Definition develop.h:343
GList * iop
Definition develop.h:285
gboolean request
Definition develop.h:425
dt_backbuf_t output_histogram
Definition develop.h:342
int32_t preview_height
Definition develop.h:213
struct dt_develop_t::@24 iso_12646
int32_t processed_width
Definition develop.h:233
struct dt_iop_module_t * gui_module
Definition develop.h:165
dt_pthread_rwlock_t history_mutex
Definition develop.h:263
GSList * samples
Definition develop.h:398
struct dt_develop_t::@20::@27 snapshot
int32_t orig_width
Definition develop.h:205
gboolean forms_changed
Definition develop.h:332
GtkWidget * floating_window
Definition develop.h:455
struct dt_dev_pixelpipe_t * preview_pipe
Definition develop.h:247
gboolean pipelines_started
Definition develop.h:351
struct dt_dev_pixelpipe_t * virtual_pipe
Definition develop.h:251
GList * alliop
Definition develop.h:287
dt_aligned_pixel_t wb_coeffs
Definition develop.h:448
struct dt_iop_module_t *struct dt_iop_color_picker_t * picker
Definition develop.h:389
GList * allforms
Definition develop.h:335
dt_backbuf_t raw_histogram
Definition develop.h:341
float scaling
Definition develop.h:193
struct dt_masks_form_gui_t * form_gui
Definition develop.h:333
struct dt_develop_t::@17 roi
struct dt_colorpicker_sample_t * selected_sample
Definition develop.h:399
struct dt_develop_t::@20 proxy
int32_t processed_height
Definition develop.h:233
int32_t preview_width
Definition develop.h:213
gboolean enabled
Definition develop.h:393
dt_pthread_rwlock_t masks_mutex
Definition develop.h:339
struct dt_develop_t::@22 rawoverexposed
const gchar * filename
Definition develop.h:426
struct dt_dev_pixelpipe_t * pipe
Definition develop.h:247
gboolean display_samples
Definition develop.h:400
GList * forms
Definition develop.h:327
struct dt_develop_t::@21 overexposed
float natural_scale
Definition develop.h:224
gboolean block_normal_pan
Definition gtk.h:188
double ppd
Definition gtk.h:205
gboolean is_dragging
Definition gtk.h:218
dt_accels_t * accels
Definition gtk.h:199
GtkWidget * has_scroll_focus
Definition gtk.h:234
gboolean is_painting
Definition gtk.h:219
dt_ui_t * ui
Definition gtk.h:165
gint64 last_time_us
Definition gtk.h:186
gboolean enabled
Definition gtk.h:187
guint timeout_source
Definition gtk.h:183
float velocity[2]
Definition gtk.h:185
struct dt_gui_gtk_t::@49 mouse
struct dt_view_t * view
Definition gtk.h:184
struct dt_gui_gtk_t::@48 pan_edge
int32_t id
Definition image.h:319
dt_iop_module_t *dt_iop_color_picker_kind_t kind
GModule *dt_dev_operation_t op
Definition imageop.h:260
gpointer blend_data
Definition imageop.h:355
struct dt_develop_blend_params_t * blend_params
Definition imageop.h:353
GModule *dt_dev_operation_t op
Definition imageop.h:286
gboolean enabled
Definition imageop.h:335
dt_iop_module_so_t * so
Definition imageop.h:396
gboolean creation
Definition masks.h:517
dt_masks_type_t type
Definition masks.h:380
GList * points
Definition masks.h:379
dt_thumbtable_t * thumbtable_lighttable
GtkWidget * main_window
dt_thumbtable_t * thumbtable_filmstrip
dt_view_t * current_view
Definition view.h:202
GtkWidget * guides_popover
Definition view.h:221
dt_darkroom_layout_t(* get_layout)(struct dt_view_t *view)
Definition view.h:246
struct dt_view_manager_t::@69 proxy
void(* set_default_cursor)(struct dt_view_t *view, double x, double y)
Definition view.h:247
struct dt_view_t * view
Definition view.h:245
GtkWidget * guides_toggle
Definition view.h:221
struct dt_view_manager_t::@69::@72 darkroom
uint32_t height
Definition view.h:160
GModule *void * data
Definition view.h:158
uint32_t width
Definition view.h:160
GtkWidget * window
typedef double((*spd)(unsigned long int wavelength, double TempK))
#define MIN(a, b)
Definition thinplate.c:32
#define MAX(a, b)
Definition thinplate.c:29
int dt_thumbtable_scroll_to_selection(dt_thumbtable_t *table)
Scroll to show selected content.
Definition thumbtable.c:444
void dt_thumbtable_update_parent(dt_thumbtable_t *table)
A widget to manage and display image thumbnails in Ansel's lighttable and filmstrip views.
#define dt_thumbtable_refresh_thumbnail(table, imgid, reinit)
Definition thumbtable.h:292
static void dt_thumbtable_show(dt_thumbtable_t *table)
Show the thumbnail table widget.
Definition thumbtable.h:389
static void dt_thumbtable_hide(dt_thumbtable_t *table)
Hide the thumbnail table widget.
Definition thumbtable.h:404
GtkWidget * dtgtk_togglebutton_new(DTGTKCairoPaintIconFunc paint, gint paintflags, void *paintdata)
void dt_undo_clear(dt_undo_t *self, uint32_t filter)
Definition undo.c:337
@ DT_UNDO_TAGS
Definition undo.h:46
@ DT_UNDO_DEVELOP
Definition undo.h:52
void dt_view_manager_module_toolbox_add(dt_view_manager_t *vm, GtkWidget *tool, dt_view_type_flags_t views)
Definition view.c:1357
void dt_view_active_images_add(int32_t imgid, gboolean raise)
Definition view.c:1307
void dt_view_active_images_reset(gboolean raise)
Definition view.c:1298
int dt_view_manager_switch(dt_view_manager_t *vm, const char *view_name)
Definition view.c:236
void dt_view_image_info_update(int32_t imgid)
Definition view.c:1501
int32_t dt_view_active_images_get_first()
Definition view.c:1337
const dt_view_t * dt_view_manager_get_current_view(dt_view_manager_t *vm)
Definition view.c:141
@ DT_VIEW_STUDIO_CAPTURE
Definition view.h:78
@ DT_VIEW_DARKROOM
Definition view.h:79
dt_darkroom_layout_t
Definition view.h:93
@ DT_DARKROOM_LAYOUT_EDITING
Definition view.h:95